c400b2cd6687bbe7e6d17a607f3dc5033ebb4c71
[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 from toscaparser.capabilities import Capability
14 from toscaparser.common.exception import ExceptionCollector
15 from toscaparser.common.exception import MissingRequiredFieldError
16 from toscaparser.common.exception import UnknownFieldError
17 from toscaparser.common.exception import ValidationError
18 from toscaparser.elements.grouptype import GroupType
19 from toscaparser.elements.interfaces import InterfacesDef
20 from toscaparser.elements.nodetype import NodeType
21 from toscaparser.elements.policytype import PolicyType
22 from toscaparser.elements.relationshiptype import RelationshipType
23 from toscaparser.properties import Property
24 from toscaparser.unsupportedtype import UnsupportedType
25 from toscaparser.utils.gettextutils import _
26 from org.openecomp.sdc.toscaparser.jython import JyEntityTemplate
27
28
29 class EntityTemplate(JyEntityTemplate):
30     '''Base class for TOSCA templates.'''
31
32     SECTIONS = (DERIVED_FROM, PROPERTIES, REQUIREMENTS,
33                 INTERFACES, CAPABILITIES, TYPE, DESCRIPTION, DIRECTIVES,
34                 ATTRIBUTES, ARTIFACTS, NODE_FILTER, COPY) = \
35                ('derived_from', 'properties', 'requirements', 'interfaces',
36                 'capabilities', 'type', 'description', 'directives',
37                 'attributes', 'artifacts', 'node_filter', 'copy')
38     REQUIREMENTS_SECTION = (NODE, CAPABILITY, RELATIONSHIP, OCCURRENCES, NODE_FILTER) = \
39                            ('node', 'capability', 'relationship',
40                             'occurrences', 'node_filter')
41     # Special key names
42     SPECIAL_SECTIONS = (METADATA) = ('metadata')
43
44     def __init__(self, name, template, entity_name, custom_def=None):
45         self.name = name
46         self.entity_tpl = template
47         self.custom_def = custom_def
48         self._validate_field(self.entity_tpl)
49         type = self.entity_tpl.get('type')
50         UnsupportedType.validate_type(type)
51         if entity_name == 'node_type':
52             self.type_definition = NodeType(type, custom_def) \
53                 if type is not None else None
54         if entity_name == 'relationship_type':
55             relationship = template.get('relationship')
56             type = None
57             if relationship and isinstance(relationship, dict):
58                 type = relationship.get('type')
59             elif isinstance(relationship, str):
60                 type = self.entity_tpl['relationship']
61             else:
62                 type = self.entity_tpl['type']
63             UnsupportedType.validate_type(type)
64             self.type_definition = RelationshipType(type,
65                                                     None, custom_def)
66         if entity_name == 'policy_type':
67             if not type:
68                 msg = (_('Policy definition of "%(pname)s" must have'
69                        ' a "type" ''attribute.') % dict(pname=name))
70                 ExceptionCollector.appendException(
71                     ValidationError(msg))
72
73             self.type_definition = PolicyType(type, custom_def)
74         if entity_name == 'group_type':
75             self.type_definition = GroupType(type, custom_def) \
76                 if type is not None else None
77         self._properties = None
78         self._interfaces = None
79         self._requirements = None
80         self._capabilities = None
81
82     def getJyName(self):
83         return self.name   
84     
85     def getJyDescription(self):
86         return self.entity_tpl.get('description')
87     
88     def getJyTypeDefinition(self):
89         return self.type_definition   
90     
91     def getJyProperties(self):
92         return self.get_properties_objects()
93     
94     def getJyCapabilities(self):
95         return self.get_capabilities_objects()  
96     
97     def getJyRequirements(self):
98         return self.requirements
99     
100     @property
101     def type(self):
102         if self.type_definition:
103             return self.type_definition.type
104
105     @property
106     def parent_type(self):
107         if self.type_definition:
108             return self.type_definition.parent_type
109
110     @property
111     def requirements(self):
112         if self._requirements is None:
113             self._requirements = self.type_definition.get_value(
114                 self.REQUIREMENTS,
115                 self.entity_tpl) or []
116         return self._requirements
117
118     def get_properties_objects(self):
119         '''Return properties objects for this template.'''
120         if self._properties is None:
121             self._properties = self._create_properties()
122         return self._properties
123
124     def get_properties(self):
125         '''Return a dictionary of property name-object pairs.'''
126         return {prop.name: prop
127                 for prop in self.get_properties_objects()}
128
129     def get_property_value(self, name):
130         '''Return the value of a given property name.'''
131         props = self.get_properties()
132         if props and name in props.keys():
133             return props[name].value
134
135     @property
136     def interfaces(self):
137         if self._interfaces is None:
138             self._interfaces = self._create_interfaces()
139         return self._interfaces
140
141     def get_capabilities_objects(self):
142         '''Return capabilities objects for this template.'''
143         if not self._capabilities:
144             self._capabilities = self._create_capabilities()
145         return self._capabilities
146
147     def get_capabilities(self):
148         '''Return a dictionary of capability name-object pairs.'''
149         return {cap.name: cap
150                 for cap in self.get_capabilities_objects()}
151
152     def is_derived_from(self, type_str):
153         '''Check if object inherits from the given type.
154
155         Returns true if this object is derived from 'type_str'.
156         False otherwise.
157         '''
158         if not self.type:
159             return False
160         elif self.type == type_str:
161             return True
162         elif self.parent_type:
163             return self.parent_type.is_derived_from(type_str)
164         else:
165             return False
166
167     def _create_capabilities(self):
168         capability = []
169         caps = self.type_definition.get_value(self.CAPABILITIES,
170                                               self.entity_tpl, True)
171         if caps:
172             for name, props in caps.items():
173                 capabilities = self.type_definition.get_capabilities()
174                 if name in capabilities.keys():
175                     c = capabilities[name]
176                     properties = {}
177                     # first use the definition default value
178                     if c.properties:
179                         for property_name in c.properties.keys():
180                             prop_def = c.properties[property_name]
181                             if 'default' in prop_def:
182                                 properties[property_name] = prop_def['default']
183                     # then update (if available) with the node properties
184                     if 'properties' in props and props['properties']:
185                         properties.update(props['properties'])
186
187                     cap = Capability(name, properties, c)
188                     capability.append(cap)
189         return capability
190
191     def _validate_properties(self, template, entitytype):
192         properties = entitytype.get_value(self.PROPERTIES, template)
193         self._common_validate_properties(entitytype, properties)
194
195     def _validate_capabilities(self):
196         type_capabilities = self.type_definition.get_capabilities()
197         allowed_caps = \
198             type_capabilities.keys() if type_capabilities else []
199         capabilities = self.type_definition.get_value(self.CAPABILITIES,
200                                                       self.entity_tpl)
201         if capabilities:
202             self._common_validate_field(capabilities, allowed_caps,
203                                         'capabilities')
204             self._validate_capabilities_properties(capabilities)
205
206     def _validate_capabilities_properties(self, capabilities):
207         for cap, props in capabilities.items():
208             capability = self.get_capability(cap)
209             if not capability:
210                 continue
211             capabilitydef = capability.definition
212             self._common_validate_properties(capabilitydef,
213                                              props[self.PROPERTIES])
214
215             # validating capability properties values
216             for prop in self.get_capability(cap).get_properties_objects():
217                 prop.validate()
218
219                 # TODO(srinivas_tadepalli): temporary work around to validate
220                 # default_instances until standardized in specification
221                 if cap == "scalable" and prop.name == "default_instances":
222                     prop_dict = props[self.PROPERTIES]
223                     min_instances = prop_dict.get("min_instances")
224                     max_instances = prop_dict.get("max_instances")
225                     default_instances = prop_dict.get("default_instances")
226                     if not (min_instances <= default_instances
227                             <= max_instances):
228                         err_msg = ('"properties" of template "%s": '
229                                    '"default_instances" value is not between '
230                                    '"min_instances" and "max_instances".' %
231                                    self.name)
232                         ExceptionCollector.appendException(
233                             ValidationError(message=err_msg))
234
235     def _common_validate_properties(self, entitytype, properties):
236         allowed_props = []
237         required_props = []
238         for p in entitytype.get_properties_def_objects():
239             allowed_props.append(p.name)
240             # If property is 'required' and has no 'default' value then record
241             if p.required and p.default is None:
242                 required_props.append(p.name)
243         # validate all required properties have values
244         if properties:
245             req_props_no_value_or_default = []
246             self._common_validate_field(properties, allowed_props,
247                                         'properties')
248             # make sure it's not missing any property required by a tosca type
249             for r in required_props:
250                 if r not in properties.keys():
251                     req_props_no_value_or_default.append(r)
252             # Required properties found without value or a default value
253             if req_props_no_value_or_default:
254                 ExceptionCollector.appendException(
255                     MissingRequiredFieldError(
256                         what='"properties" of template "%s"' % self.name,
257                         required=req_props_no_value_or_default))
258         else:
259             # Required properties in schema, but not in template
260             if required_props:
261                 ExceptionCollector.appendException(
262                     MissingRequiredFieldError(
263                         what='"properties" of template "%s"' % self.name,
264                         required=required_props))
265
266     def _validate_field(self, template):
267         if not isinstance(template, dict):
268             ExceptionCollector.appendException(
269                 MissingRequiredFieldError(
270                     what='Template "%s"' % self.name, required=self.TYPE))
271         try:
272             relationship = template.get('relationship')
273             if relationship and not isinstance(relationship, str):
274                 relationship[self.TYPE]
275             elif isinstance(relationship, str):
276                 template['relationship']
277             else:
278                 template[self.TYPE]
279         except KeyError:
280             ExceptionCollector.appendException(
281                 MissingRequiredFieldError(
282                     what='Template "%s"' % self.name, required=self.TYPE))
283
284     def _common_validate_field(self, schema, allowedlist, section):
285         for name in schema:
286             if name not in allowedlist:
287                 ExceptionCollector.appendException(
288                     UnknownFieldError(
289                         what=('"%(section)s" of template "%(nodename)s"'
290                               % {'section': section, 'nodename': self.name}),
291                         field=name))
292
293     def _create_properties(self):
294         props = []
295         properties = self.type_definition.get_value(self.PROPERTIES,
296                                                     self.entity_tpl) or {}
297         for name, value in properties.items():
298             props_def = self.type_definition.get_properties_def()
299             if props_def and name in props_def:
300                 prop = Property(name, value,
301                                 props_def[name].schema, self.custom_def)
302                 props.append(prop)
303         for p in self.type_definition.get_properties_def_objects():
304             if p.default is not None and p.name not in properties.keys():
305                 prop = Property(p.name, p.default, p.schema, self.custom_def)
306                 props.append(prop)
307         return props
308
309     def _create_interfaces(self):
310         interfaces = []
311         type_interfaces = None
312         if isinstance(self.type_definition, RelationshipType):
313             if isinstance(self.entity_tpl, dict):
314                 if self.INTERFACES in self.entity_tpl:
315                     type_interfaces = self.entity_tpl[self.INTERFACES]
316                 else:
317                     for rel_def, value in self.entity_tpl.items():
318                         if rel_def != 'type':
319                             rel_def = self.entity_tpl.get(rel_def)
320                             rel = None
321                             if isinstance(rel_def, dict):
322                                 rel = rel_def.get('relationship')
323                             if rel:
324                                 if self.INTERFACES in rel:
325                                     type_interfaces = rel[self.INTERFACES]
326                                     break
327         else:
328             type_interfaces = self.type_definition.get_value(self.INTERFACES,
329                                                              self.entity_tpl)
330         if type_interfaces:
331             for interface_type, value in type_interfaces.items():
332                 for op, op_def in value.items():
333                     iface = InterfacesDef(self.type_definition,
334                                           interfacetype=interface_type,
335                                           node_template=self,
336                                           name=op,
337                                           value=op_def)
338                     interfaces.append(iface)
339         return interfaces
340
341     def get_capability(self, name):
342         """Provide named capability
343
344         :param name: name of capability
345         :return: capability object if found, None otherwise
346         """
347         caps = self.get_capabilities()
348         if caps and name in caps.keys():
349             return caps[name]