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
5 # http://www.apache.org/licenses/LICENSE-2.0
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
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 _
28 class EntityTemplate(object):
29 '''Base class for TOSCA templates.'''
31 SECTIONS = (DERIVED_FROM, PROPERTIES, REQUIREMENTS,
32 INTERFACES, CAPABILITIES, TYPE, DESCRIPTION, DIRECTIVES,
33 ATTRIBUTES, ARTIFACTS, NODE_FILTER, COPY) = \
34 ('derived_from', 'properties', 'requirements', 'interfaces',
35 'capabilities', 'type', 'description', 'directives',
36 'attributes', 'artifacts', 'node_filter', 'copy')
37 REQUIREMENTS_SECTION = (NODE, CAPABILITY, RELATIONSHIP, OCCURRENCES, NODE_FILTER) = \
38 ('node', 'capability', 'relationship',
39 'occurrences', 'node_filter')
41 SPECIAL_SECTIONS = (METADATA) = ('metadata')
43 def __init__(self, name, template, entity_name, custom_def=None):
45 self.entity_tpl = template
46 self.custom_def = custom_def
47 self._validate_field(self.entity_tpl)
48 type = self.entity_tpl.get('type')
49 UnsupportedType.validate_type(type)
50 if entity_name == 'node_type':
51 self.type_definition = NodeType(type, custom_def) \
52 if type is not None else None
53 if entity_name == 'relationship_type':
54 relationship = template.get('relationship')
56 if relationship and isinstance(relationship, dict):
57 type = relationship.get('type')
58 elif isinstance(relationship, str):
59 type = self.entity_tpl['relationship']
61 type = self.entity_tpl['type']
62 UnsupportedType.validate_type(type)
63 self.type_definition = RelationshipType(type,
65 if entity_name == 'policy_type':
67 msg = (_('Policy definition of "%(pname)s" must have'
68 ' a "type" ''attribute.') % dict(pname=name))
69 ExceptionCollector.appendException(
71 self.type_definition = PolicyType(type, custom_def)
72 if entity_name == 'group_type':
73 self.type_definition = GroupType(type, custom_def) \
74 if type is not None else None
75 self._properties = None
76 self._interfaces = None
77 self._requirements = None
78 self._capabilities = None
82 if self.type_definition:
83 return self.type_definition.type
86 def parent_type(self):
87 if self.type_definition:
88 return self.type_definition.parent_type
91 def requirements(self):
92 if self._requirements is None:
93 self._requirements = self.type_definition.get_value(
95 self.entity_tpl) or []
96 return self._requirements
98 def get_properties_objects(self):
99 '''Return properties objects for this template.'''
100 if self._properties is None:
101 self._properties = self._create_properties()
102 return self._properties
104 def get_properties(self):
105 '''Return a dictionary of property name-object pairs.'''
106 return {prop.name: prop
107 for prop in self.get_properties_objects()}
109 def get_property_value(self, name):
110 '''Return the value of a given property name.'''
111 props = self.get_properties()
112 if props and name in props.keys():
113 return props[name].value
116 def interfaces(self):
117 if self._interfaces is None:
118 self._interfaces = self._create_interfaces()
119 return self._interfaces
121 def get_capabilities_objects(self):
122 '''Return capabilities objects for this template.'''
123 if not self._capabilities:
124 self._capabilities = self._create_capabilities()
125 return self._capabilities
127 def get_capabilities(self):
128 '''Return a dictionary of capability name-object pairs.'''
129 return {cap.name: cap
130 for cap in self.get_capabilities_objects()}
132 def is_derived_from(self, type_str):
133 '''Check if object inherits from the given type.
135 Returns true if this object is derived from 'type_str'.
140 elif self.type == type_str:
142 elif self.parent_type:
143 return self.parent_type.is_derived_from(type_str)
147 def _create_capabilities(self):
149 caps = self.type_definition.get_value(self.CAPABILITIES,
150 self.entity_tpl, True)
152 for name, props in caps.items():
153 capabilities = self.type_definition.get_capabilities()
154 if name in capabilities.keys():
155 c = capabilities[name]
157 # first use the definition default value
159 for property_name in c.properties.keys():
160 prop_def = c.properties[property_name]
161 if 'default' in prop_def:
162 properties[property_name] = prop_def['default']
163 # then update (if available) with the node properties
164 if 'properties' in props and props['properties']:
165 properties.update(props['properties'])
167 cap = Capability(name, properties, c)
168 capability.append(cap)
171 def _validate_properties(self, template, entitytype):
172 properties = entitytype.get_value(self.PROPERTIES, template)
173 self._common_validate_properties(entitytype, properties)
175 def _validate_capabilities(self):
176 type_capabilities = self.type_definition.get_capabilities()
178 type_capabilities.keys() if type_capabilities else []
179 capabilities = self.type_definition.get_value(self.CAPABILITIES,
182 self._common_validate_field(capabilities, allowed_caps,
184 self._validate_capabilities_properties(capabilities)
186 def _validate_capabilities_properties(self, capabilities):
187 for cap, props in capabilities.items():
188 capability = self.get_capability(cap)
191 capabilitydef = capability.definition
192 self._common_validate_properties(capabilitydef,
193 props[self.PROPERTIES])
195 # validating capability properties values
196 for prop in self.get_capability(cap).get_properties_objects():
199 # TODO(srinivas_tadepalli): temporary work around to validate
200 # default_instances until standardized in specification
201 if cap == "scalable" and prop.name == "default_instances":
202 prop_dict = props[self.PROPERTIES]
203 min_instances = prop_dict.get("min_instances")
204 max_instances = prop_dict.get("max_instances")
205 default_instances = prop_dict.get("default_instances")
206 if not (min_instances <= default_instances
208 err_msg = ('"properties" of template "%s": '
209 '"default_instances" value is not between '
210 '"min_instances" and "max_instances".' %
212 ExceptionCollector.appendException(
213 ValidationError(message=err_msg))
215 def _common_validate_properties(self, entitytype, properties):
218 for p in entitytype.get_properties_def_objects():
219 allowed_props.append(p.name)
220 # If property is 'required' and has no 'default' value then record
221 if p.required and p.default is None:
222 required_props.append(p.name)
223 # validate all required properties have values
225 req_props_no_value_or_default = []
226 self._common_validate_field(properties, allowed_props,
228 # make sure it's not missing any property required by a tosca type
229 for r in required_props:
230 if r not in properties.keys():
231 req_props_no_value_or_default.append(r)
232 # Required properties found without value or a default value
233 if req_props_no_value_or_default:
234 ExceptionCollector.appendException(
235 MissingRequiredFieldError(
236 what='"properties" of template "%s"' % self.name,
237 required=req_props_no_value_or_default))
239 # Required properties in schema, but not in template
241 ExceptionCollector.appendException(
242 MissingRequiredFieldError(
243 what='"properties" of template "%s"' % self.name,
244 required=required_props))
246 def _validate_field(self, template):
247 if not isinstance(template, dict):
248 ExceptionCollector.appendException(
249 MissingRequiredFieldError(
250 what='Template "%s"' % self.name, required=self.TYPE))
252 relationship = template.get('relationship')
253 if relationship and not isinstance(relationship, str):
254 relationship[self.TYPE]
255 elif isinstance(relationship, str):
256 template['relationship']
260 ExceptionCollector.appendException(
261 MissingRequiredFieldError(
262 what='Template "%s"' % self.name, required=self.TYPE))
264 def _common_validate_field(self, schema, allowedlist, section):
266 if name not in allowedlist:
267 ExceptionCollector.appendException(
269 what=('"%(section)s" of template "%(nodename)s"'
270 % {'section': section, 'nodename': self.name}),
273 def _create_properties(self):
275 properties = self.type_definition.get_value(self.PROPERTIES,
276 self.entity_tpl) or {}
277 for name, value in properties.items():
278 props_def = self.type_definition.get_properties_def()
279 if props_def and name in props_def:
280 prop = Property(name, value,
281 props_def[name].schema, self.custom_def)
283 for p in self.type_definition.get_properties_def_objects():
284 if p.default is not None and p.name not in properties.keys():
285 prop = Property(p.name, p.default, p.schema, self.custom_def)
289 def _create_interfaces(self):
291 type_interfaces = None
292 if isinstance(self.type_definition, RelationshipType):
293 if isinstance(self.entity_tpl, dict):
294 if self.INTERFACES in self.entity_tpl:
295 type_interfaces = self.entity_tpl[self.INTERFACES]
297 for rel_def, value in self.entity_tpl.items():
298 if rel_def != 'type':
299 rel_def = self.entity_tpl.get(rel_def)
301 if isinstance(rel_def, dict):
302 rel = rel_def.get('relationship')
304 if self.INTERFACES in rel:
305 type_interfaces = rel[self.INTERFACES]
308 type_interfaces = self.type_definition.get_value(self.INTERFACES,
311 for interface_type, value in type_interfaces.items():
312 for op, op_def in value.items():
313 iface = InterfacesDef(self.type_definition,
314 interfacetype=interface_type,
318 interfaces.append(iface)
321 def get_capability(self, name):
322 """Provide named capability
324 :param name: name of capability
325 :return: capability object if found, None otherwise
327 caps = self.get_capabilities()
328 if caps and name in caps.keys():