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