Merge "Release D doc update"
[parser.git] / tosca2heat / tosca-parser / toscaparser / nodetemplate.py
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
34 log = logging.getLogger('tosca')
35
36
37 class NodeTemplate(EntityTemplate):
38     '''Node template from a Tosca profile.'''
39     def __init__(self, name, node_templates, custom_def=None,
40                  available_rel_tpls=None, available_rel_types=None):
41         super(NodeTemplate, self).__init__(name, node_templates[name],
42                                            'node_type',
43                                            custom_def)
44         self.templates = node_templates
45         self._validate_fields(node_templates[name])
46         self.custom_def = custom_def
47         self.related = {}
48         self.relationship_tpl = []
49         self.available_rel_tpls = available_rel_tpls
50         self.available_rel_types = available_rel_types
51         self._relationships = {}
52         self.substitution_mapped = None
53
54     @property
55     def relationships(self):
56         if not self._relationships:
57             requires = self.requirements
58             if requires and isinstance(requires, list):
59                 for r in requires:
60                     for r1, value in r.items():
61                         explicit = self._get_explicit_relationship(r, value)
62                         if explicit:
63                             for key, value in explicit.items():
64                                 self._relationships[key] = value
65         return self._relationships
66
67     def _get_explicit_relationship(self, req, value):
68         """Handle explicit relationship
69
70         For example,
71         - req:
72             node: DBMS
73             relationship: tosca.relationships.HostedOn
74         """
75         explicit_relation = {}
76         node = value.get('node') if isinstance(value, dict) else value
77
78         if node:
79             # TODO(spzala) implement look up once Glance meta data is available
80             # to find a matching TOSCA node using the TOSCA types
81             msg = _('Lookup by TOSCA types is not supported. '
82                     'Requirement for "%s" can not be full-filled.') % self.name
83             if (node in list(self.type_definition.TOSCA_DEF.keys())
84                or node in self.custom_def):
85                 ExceptionCollector.appendException(NotImplementedError(msg))
86                 return
87
88             if node not in self.templates:
89                 ExceptionCollector.appendException(
90                     KeyError(_('Node template "%s" was not found.') % node))
91                 return
92
93             related_tpl = NodeTemplate(node, self.templates, self.custom_def)
94             relationship = value.get('relationship') \
95                 if isinstance(value, dict) else None
96             # check if it's type has relationship defined
97             if not relationship:
98                 parent_reqs = self.type_definition.get_all_requirements()
99                 if parent_reqs is None:
100                     ExceptionCollector.appendException(
101                         ValidationError(message='parent_req is ' +
102                                         str(parent_reqs)))
103                 else:
104                     for key in req.keys():
105                         for req_dict in parent_reqs:
106                             if key in req_dict.keys():
107                                 relationship = (req_dict.get(key).
108                                                 get('relationship'))
109                                 break
110             if relationship:
111                 found_relationship_tpl = False
112                 # apply available relationship templates if found
113                 if self.available_rel_tpls:
114                     for tpl in self.available_rel_tpls:
115                         if tpl.name == relationship:
116                             rtype = RelationshipType(tpl.type, None,
117                                                      self.custom_def)
118                             explicit_relation[rtype] = related_tpl
119                             tpl.target = related_tpl
120                             tpl.source = self
121                             self.relationship_tpl.append(tpl)
122                             found_relationship_tpl = True
123                 # create relationship template object.
124                 rel_prfx = self.type_definition.RELATIONSHIP_PREFIX
125                 if not found_relationship_tpl:
126                     if isinstance(relationship, dict):
127                         relationship = relationship.get('type')
128                         if relationship:
129                             if self.available_rel_types and \
130                                relationship in self.available_rel_types.keys():
131                                 pass
132                             elif not relationship.startswith(rel_prfx):
133                                 relationship = rel_prfx + relationship
134                         else:
135                             ExceptionCollector.appendException(
136                                 MissingRequiredFieldError(
137                                     what=_('"relationship" used in template '
138                                            '"%s"') % related_tpl.name,
139                                     required=self.TYPE))
140                     for rtype in self.type_definition.relationship.keys():
141                         if rtype.type == relationship:
142                             explicit_relation[rtype] = related_tpl
143                             related_tpl._add_relationship_template(req,
144                                                                    rtype.type,
145                                                                    self)
146                         elif self.available_rel_types:
147                             if relationship in self.available_rel_types.keys():
148                                 rel_type_def = self.available_rel_types.\
149                                     get(relationship)
150                                 if 'derived_from' in rel_type_def:
151                                     super_type = \
152                                         rel_type_def.get('derived_from')
153                                     if not super_type.startswith(rel_prfx):
154                                         super_type = rel_prfx + super_type
155                                     if rtype.type == super_type:
156                                         explicit_relation[rtype] = related_tpl
157                                         related_tpl.\
158                                             _add_relationship_template(
159                                                 req, rtype.type, self)
160         return explicit_relation
161
162     def _add_relationship_template(self, requirement, rtype, source):
163         req = requirement.copy()
164         req['type'] = rtype
165         tpl = RelationshipTemplate(req, rtype, self.custom_def, self, source)
166         self.relationship_tpl.append(tpl)
167
168     def get_relationship_template(self):
169         return self.relationship_tpl
170
171     def _add_next(self, nodetpl, relationship):
172         self.related[nodetpl] = relationship
173
174     @property
175     def related_nodes(self):
176         if not self.related:
177             for relation, node in self.type_definition.relationship.items():
178                 for tpl in self.templates:
179                     if tpl == node.type:
180                         self.related[NodeTemplate(tpl)] = relation
181         return self.related.keys()
182
183     def validate(self, tosca_tpl=None):
184         self._validate_capabilities()
185         self._validate_requirements()
186         self._validate_properties(self.entity_tpl, self.type_definition)
187         self._validate_interfaces()
188         for prop in self.get_properties_objects():
189             prop.validate()
190
191     def _validate_requirements(self):
192         type_requires = self.type_definition.get_all_requirements()
193         allowed_reqs = ["template"]
194         if type_requires:
195             for treq in type_requires:
196                 for key, value in treq.items():
197                     allowed_reqs.append(key)
198                     if isinstance(value, dict):
199                         for key in value:
200                             allowed_reqs.append(key)
201
202         requires = self.type_definition.get_value(self.REQUIREMENTS,
203                                                   self.entity_tpl)
204         if requires:
205             if not isinstance(requires, list):
206                 ExceptionCollector.appendException(
207                     TypeMismatchError(
208                         what='"requirements" of template "%s"' % self.name,
209                         type='list'))
210             else:
211                 for req in requires:
212                     for r1, value in req.items():
213                         if isinstance(value, dict):
214                             self._validate_requirements_keys(value)
215                             self._validate_requirements_properties(value)
216                             allowed_reqs.append(r1)
217                     self._common_validate_field(req, allowed_reqs,
218                                                 'requirements')
219
220     def _validate_requirements_properties(self, requirements):
221         # TODO(anyone): Only occurrences property of the requirements is
222         # validated here. Validation of other requirement properties are being
223         # validated in different files. Better to keep all the requirements
224         # properties validation here.
225         for key, value in requirements.items():
226             if key == 'occurrences':
227                 self._validate_occurrences(value)
228                 break
229
230     def _validate_occurrences(self, occurrences):
231         DataEntity.validate_datatype('list', occurrences)
232         for value in occurrences:
233             DataEntity.validate_datatype('integer', value)
234         if len(occurrences) != 2 or not (0 <= occurrences[0] <= occurrences[1]) \
235                 or occurrences[1] == 0:
236             ExceptionCollector.appendException(
237                 InvalidPropertyValueError(what=(occurrences)))
238
239     def _validate_requirements_keys(self, requirement):
240         for key in requirement.keys():
241             if key not in self.REQUIREMENTS_SECTION:
242                 ExceptionCollector.appendException(
243                     UnknownFieldError(
244                         what='"requirements" of template "%s"' % self.name,
245                         field=key))
246
247     def _validate_interfaces(self):
248         ifaces = self.type_definition.get_value(self.INTERFACES,
249                                                 self.entity_tpl)
250         if ifaces:
251             for name, value in ifaces.items():
252                 if name in (LIFECYCLE, LIFECYCLE_SHORTNAME):
253                     self._common_validate_field(
254                         value, InterfacesDef.
255                         interfaces_node_lifecycle_operations,
256                         'interfaces')
257                 elif name in (CONFIGURE, CONFIGURE_SHORTNAME):
258                     self._common_validate_field(
259                         value, InterfacesDef.
260                         interfaces_relationship_configure_operations,
261                         'interfaces')
262                 elif name in self.type_definition.interfaces.keys():
263                     self._common_validate_field(
264                         value,
265                         self._collect_custom_iface_operations(name),
266                         'interfaces')
267                 else:
268                     ExceptionCollector.appendException(
269                         UnknownFieldError(
270                             what='"interfaces" of template "%s"' %
271                             self.name, field=name))
272
273     def _collect_custom_iface_operations(self, name):
274         allowed_operations = []
275         nodetype_iface_def = self.type_definition.interfaces[name]
276         allowed_operations.extend(nodetype_iface_def.keys())
277         if 'type' in nodetype_iface_def:
278             iface_type = nodetype_iface_def['type']
279             if iface_type in self.type_definition.custom_def:
280                 iface_type_def = self.type_definition.custom_def[iface_type]
281             else:
282                 iface_type_def = self.type_definition.TOSCA_DEF[iface_type]
283             allowed_operations.extend(iface_type_def.keys())
284         allowed_operations = [op for op in allowed_operations if
285                               op not in INTERFACE_DEF_RESERVED_WORDS]
286         return allowed_operations
287
288     def _validate_fields(self, nodetemplate):
289         for name in nodetemplate.keys():
290             if name not in self.SECTIONS and name not in self.SPECIAL_SECTIONS:
291                 ExceptionCollector.appendException(
292                     UnknownFieldError(what='Node template "%s"' % self.name,
293                                       field=name))