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
86 def requirements(self):
87 if self._requirements is None:
88 self._requirements = self.type_definition.get_value(
90 self.entity_tpl) or []
91 return self._requirements
93 def get_properties_objects(self):
94 '''Return properties objects for this template.'''
95 if self._properties is None:
96 self._properties = self._create_properties()
97 return self._properties
99 def get_properties(self):
100 '''Return a dictionary of property name-object pairs.'''
101 return {prop.name: prop
102 for prop in self.get_properties_objects()}
104 def get_property_value(self, name):
105 '''Return the value of a given property name.'''
106 props = self.get_properties()
107 if props and name in props.keys():
108 return props[name].value
111 def interfaces(self):
112 if self._interfaces is None:
113 self._interfaces = self._create_interfaces()
114 return self._interfaces
116 def get_capabilities_objects(self):
117 '''Return capabilities objects for this template.'''
118 if not self._capabilities:
119 self._capabilities = self._create_capabilities()
120 return self._capabilities
122 def get_capabilities(self):
123 '''Return a dictionary of capability name-object pairs.'''
124 return {cap.name: cap
125 for cap in self.get_capabilities_objects()}
127 def is_derived_from(self, type_str):
128 '''Check if object inherits from the given type.
130 Returns true if this object is derived from 'type_str'.
135 elif self.type == type_str:
137 elif self.parent_type:
138 return self.parent_type.is_derived_from(type_str)
142 def _create_capabilities(self):
144 caps = self.type_definition.get_value(self.CAPABILITIES,
145 self.entity_tpl, True)
147 for name, props in caps.items():
148 capabilities = self.type_definition.get_capabilities()
149 if name in capabilities.keys():
150 c = capabilities[name]
152 # first use the definition default value
154 for property_name in c.properties.keys():
155 prop_def = c.properties[property_name]
156 if 'default' in prop_def:
157 properties[property_name] = prop_def['default']
158 # then update (if available) with the node properties
159 if 'properties' in props and props['properties']:
160 properties.update(props['properties'])
162 cap = Capability(name, properties, c)
163 capability.append(cap)
166 def _validate_properties(self, template, entitytype):
167 properties = entitytype.get_value(self.PROPERTIES, template)
168 self._common_validate_properties(entitytype, properties)
170 def _validate_capabilities(self):
171 type_capabilities = self.type_definition.get_capabilities()
173 type_capabilities.keys() if type_capabilities else []
174 capabilities = self.type_definition.get_value(self.CAPABILITIES,
177 self._common_validate_field(capabilities, allowed_caps,
179 self._validate_capabilities_properties(capabilities)
181 def _validate_capabilities_properties(self, capabilities):
182 for cap, props in capabilities.items():
183 capabilitydef = self.get_capability(cap).definition
184 self._common_validate_properties(capabilitydef,
185 props[self.PROPERTIES])
187 # validating capability properties values
188 for prop in self.get_capability(cap).get_properties_objects():
191 # TODO(srinivas_tadepalli): temporary work around to validate
192 # default_instances until standardized in specification
193 if cap == "scalable" and prop.name == "default_instances":
194 prop_dict = props[self.PROPERTIES]
195 min_instances = prop_dict.get("min_instances")
196 max_instances = prop_dict.get("max_instances")
197 default_instances = prop_dict.get("default_instances")
198 if not (min_instances <= default_instances
200 err_msg = ('"properties" of template "%s": '
201 '"default_instances" value is not between '
202 '"min_instances" and "max_instances".' %
204 ExceptionCollector.appendException(
205 ValidationError(message=err_msg))
207 def _common_validate_properties(self, entitytype, properties):
210 for p in entitytype.get_properties_def_objects():
211 allowed_props.append(p.name)
212 # If property is 'required' and has no 'default' value then record
213 if p.required and p.default is None:
214 required_props.append(p.name)
215 # validate all required properties have values
217 req_props_no_value_or_default = []
218 self._common_validate_field(properties, allowed_props,
220 # make sure it's not missing any property required by a tosca type
221 for r in required_props:
222 if r not in properties.keys():
223 req_props_no_value_or_default.append(r)
224 # Required properties found without value or a default value
225 if req_props_no_value_or_default:
226 ExceptionCollector.appendException(
227 MissingRequiredFieldError(
228 what='"properties" of template "%s"' % self.name,
229 required=req_props_no_value_or_default))
231 # Required properties in schema, but not in template
233 ExceptionCollector.appendException(
234 MissingRequiredFieldError(
235 what='"properties" of template "%s"' % self.name,
236 required=required_props))
238 def _validate_field(self, template):
239 if not isinstance(template, dict):
240 ExceptionCollector.appendException(
241 MissingRequiredFieldError(
242 what='Template "%s"' % self.name, required=self.TYPE))
244 relationship = template.get('relationship')
245 if relationship and not isinstance(relationship, str):
246 relationship[self.TYPE]
247 elif isinstance(relationship, str):
248 template['relationship']
252 ExceptionCollector.appendException(
253 MissingRequiredFieldError(
254 what='Template "%s"' % self.name, required=self.TYPE))
256 def _common_validate_field(self, schema, allowedlist, section):
258 if name not in allowedlist:
259 ExceptionCollector.appendException(
261 what=('"%(section)s" of template "%(nodename)s"'
262 % {'section': section, 'nodename': self.name}),
265 def _create_properties(self):
267 properties = self.type_definition.get_value(self.PROPERTIES,
268 self.entity_tpl) or {}
269 for name, value in properties.items():
270 props_def = self.type_definition.get_properties_def()
271 if props_def and name in props_def:
272 prop = Property(name, value,
273 props_def[name].schema, self.custom_def)
275 for p in self.type_definition.get_properties_def_objects():
276 if p.default is not None and p.name not in properties.keys():
277 prop = Property(p.name, p.default, p.schema, self.custom_def)
281 def _create_interfaces(self):
283 type_interfaces = None
284 if isinstance(self.type_definition, RelationshipType):
285 if isinstance(self.entity_tpl, dict):
286 if self.INTERFACES in self.entity_tpl:
287 type_interfaces = self.entity_tpl[self.INTERFACES]
289 for rel_def, value in self.entity_tpl.items():
290 if rel_def != 'type':
291 rel_def = self.entity_tpl.get(rel_def)
293 if isinstance(rel_def, dict):
294 rel = rel_def.get('relationship')
296 if self.INTERFACES in rel:
297 type_interfaces = rel[self.INTERFACES]
300 type_interfaces = self.type_definition.get_value(self.INTERFACES,
303 for interface_type, value in type_interfaces.items():
304 for op, op_def in value.items():
305 iface = InterfacesDef(self.type_definition,
306 interfacetype=interface_type,
310 interfaces.append(iface)
313 def get_capability(self, name):
314 """Provide named capability
316 :param name: name of capability
317 :return: capability object if found, None otherwise
319 caps = self.get_capabilities()
320 if caps and name in caps.keys():