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.utils.gettextutils import _
27 class EntityTemplate(object):
28 '''Base class for TOSCA templates.'''
30 SECTIONS = (DERIVED_FROM, PROPERTIES, REQUIREMENTS,
31 INTERFACES, CAPABILITIES, TYPE, DESCRIPTION, DIRECTIVES,
32 ATTRIBUTES, ARTIFACTS, NODE_FILTER, COPY) = \
33 ('derived_from', 'properties', 'requirements', 'interfaces',
34 'capabilities', 'type', 'description', 'directives',
35 'attributes', 'artifacts', 'node_filter', 'copy')
36 REQUIREMENTS_SECTION = (NODE, CAPABILITY, RELATIONSHIP, OCCURRENCES, NODE_FILTER) = \
37 ('node', 'capability', 'relationship',
38 'occurrences', 'node_filter')
40 SPECIAL_SECTIONS = (METADATA) = ('metadata')
42 def __init__(self, name, template, entity_name, custom_def=None):
44 self.entity_tpl = template
45 self.custom_def = custom_def
46 self._validate_field(self.entity_tpl)
47 if entity_name == 'node_type':
48 type = self.entity_tpl.get('type')
49 self.type_definition = NodeType(type, custom_def) \
50 if type is not None else None
51 if entity_name == 'relationship_type':
52 relationship = template.get('relationship')
54 if relationship and isinstance(relationship, dict):
55 type = relationship.get('type')
56 elif isinstance(relationship, str):
57 type = self.entity_tpl['relationship']
59 type = self.entity_tpl['type']
60 self.type_definition = RelationshipType(type,
62 if entity_name == 'policy_type':
63 type = self.entity_tpl.get('type')
65 msg = (_('Policy definition of "%(pname)s" must have'
66 ' a "type" ''attribute.') % dict(pname=name))
67 ExceptionCollector.appendException(
70 self.type_definition = PolicyType(type, custom_def)
71 if entity_name == 'group_type':
72 type = self.entity_tpl.get('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
88 def parent_type(self):
89 if self.type_definition:
90 return self.type_definition.parent_type
95 def requirements(self):
96 if self._requirements is None:
97 self._requirements = self.type_definition.get_value(
99 self.entity_tpl) or []
100 return self._requirements
102 def get_properties_objects(self):
103 '''Return properties objects for this template.'''
104 if self._properties is None:
105 self._properties = self._create_properties()
106 return self._properties
108 def get_properties(self):
109 '''Return a dictionary of property name-object pairs.'''
110 return {prop.name: prop
111 for prop in self.get_properties_objects()}
113 def get_property_value(self, name):
114 '''Return the value of a given property name.'''
115 props = self.get_properties()
116 if props and name in props.keys():
117 return props[name].value
120 def interfaces(self):
121 if self._interfaces is None:
122 self._interfaces = self._create_interfaces()
123 return self._interfaces
125 def get_capabilities_objects(self):
126 '''Return capabilities objects for this template.'''
127 if not self._capabilities:
128 self._capabilities = self._create_capabilities()
129 return self._capabilities
131 def get_capabilities(self):
132 '''Return a dictionary of capability name-object pairs.'''
133 return {cap.name: cap
134 for cap in self.get_capabilities_objects()}
136 def is_derived_from(self, type_str):
137 '''Check if object inherits from the given type.
139 Returns true if this object is derived from 'type_str'.
144 elif self.type == type_str:
146 elif self.parent_type:
147 return self.parent_type.is_derived_from(type_str)
151 def _create_capabilities(self):
153 caps = self.type_definition.get_value(self.CAPABILITIES,
154 self.entity_tpl, True)
156 for name, props in caps.items():
157 capabilities = self.type_definition.get_capabilities()
158 if name in capabilities.keys():
159 c = capabilities[name]
161 # first use the definition default value
163 for property_name in c.properties.keys():
164 prop_def = c.properties[property_name]
165 if 'default' in prop_def:
166 properties[property_name] = prop_def['default']
167 # then update (if available) with the node properties
168 if 'properties' in props and props['properties']:
169 properties.update(props['properties'])
171 cap = Capability(name, properties, c)
172 capability.append(cap)
175 def _validate_properties(self, template, entitytype):
176 properties = entitytype.get_value(self.PROPERTIES, template)
177 self._common_validate_properties(entitytype, properties)
179 def _validate_capabilities(self):
180 type_capabilities = self.type_definition.get_capabilities()
182 type_capabilities.keys() if type_capabilities else []
183 capabilities = self.type_definition.get_value(self.CAPABILITIES,
186 self._common_validate_field(capabilities, allowed_caps,
188 self._validate_capabilities_properties(capabilities)
190 def _validate_capabilities_properties(self, capabilities):
191 for cap, props in capabilities.items():
192 capabilitydef = self.get_capability(cap).definition
193 self._common_validate_properties(capabilitydef,
194 props[self.PROPERTIES])
196 # validating capability properties values
197 for prop in self.get_capability(cap).get_properties_objects():
200 # TODO(srinivas_tadepalli): temporary work around to validate
201 # default_instances until standardized in specification
202 if cap == "scalable" and prop.name == "default_instances":
203 prop_dict = props[self.PROPERTIES]
204 min_instances = prop_dict.get("min_instances")
205 max_instances = prop_dict.get("max_instances")
206 default_instances = prop_dict.get("default_instances")
207 if not (min_instances <= default_instances
209 err_msg = ('"properties" of template "%s": '
210 '"default_instances" value is not between '
211 '"min_instances" and "max_instances".' %
213 ExceptionCollector.appendException(
214 ValidationError(message=err_msg))
216 def _common_validate_properties(self, entitytype, properties):
219 for p in entitytype.get_properties_def_objects():
220 allowed_props.append(p.name)
221 # If property is 'required' and has no 'default' value then record
222 if p.required and p.default is None:
223 required_props.append(p.name)
224 # validate all required properties have values
226 req_props_no_value_or_default = []
227 self._common_validate_field(properties, allowed_props,
229 # make sure it's not missing any property required by a tosca type
230 for r in required_props:
231 if r not in properties.keys():
232 req_props_no_value_or_default.append(r)
233 # Required properties found without value or a default value
234 if req_props_no_value_or_default:
235 ExceptionCollector.appendException(
236 MissingRequiredFieldError(
237 what='"properties" of template "%s"' % self.name,
238 required=req_props_no_value_or_default))
240 # Required properties in schema, but not in template
242 ExceptionCollector.appendException(
243 MissingRequiredFieldError(
244 what='"properties" of template "%s"' % self.name,
245 required=required_props))
247 def _validate_field(self, template):
248 if not isinstance(template, dict):
249 ExceptionCollector.appendException(
250 MissingRequiredFieldError(
251 what='Template "%s"' % self.name, required=self.TYPE))
253 relationship = template.get('relationship')
254 if relationship and not isinstance(relationship, str):
255 relationship[self.TYPE]
256 elif isinstance(relationship, str):
257 template['relationship']
261 ExceptionCollector.appendException(
262 MissingRequiredFieldError(
263 what='Template "%s"' % self.name, required=self.TYPE))
265 def _common_validate_field(self, schema, allowedlist, section):
267 if name not in allowedlist:
268 ExceptionCollector.appendException(
270 what=('"%(section)s" of template "%(nodename)s"'
271 % {'section': section, 'nodename': self.name}),
274 def _create_properties(self):
276 properties = self.type_definition.get_value(self.PROPERTIES,
277 self.entity_tpl) or {}
278 for name, value in properties.items():
279 props_def = self.type_definition.get_properties_def()
280 if props_def and name in props_def:
281 prop = Property(name, value,
282 props_def[name].schema, self.custom_def)
284 for p in self.type_definition.get_properties_def_objects():
285 if p.default is not None and p.name not in properties.keys():
286 prop = Property(p.name, p.default, p.schema, self.custom_def)
290 def _create_interfaces(self):
292 type_interfaces = None
293 if isinstance(self.type_definition, RelationshipType):
294 if isinstance(self.entity_tpl, dict):
295 if self.INTERFACES in self.entity_tpl:
296 type_interfaces = self.entity_tpl[self.INTERFACES]
298 for rel_def, value in self.entity_tpl.items():
299 if rel_def != 'type':
300 rel_def = self.entity_tpl.get(rel_def)
302 if isinstance(rel_def, dict):
303 rel = rel_def.get('relationship')
305 if self.INTERFACES in rel:
306 type_interfaces = rel[self.INTERFACES]
309 type_interfaces = self.type_definition.get_value(self.INTERFACES,
312 for interface_type, value in type_interfaces.items():
313 for op, op_def in value.items():
314 iface = InterfacesDef(self.type_definition,
315 interfacetype=interface_type,
319 interfaces.append(iface)
322 def get_capability(self, name):
323 """Provide named capability
325 :param name: name of capability
326 :return: capability object if found, None otherwise
328 caps = self.get_capabilities()
329 if caps and name in caps.keys():