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
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 _
33 log = logging.getLogger('tosca')
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],
43 self.templates = node_templates
44 self._validate_fields(node_templates[name])
45 self.custom_def = custom_def
47 self.relationship_tpl = []
48 self.available_rel_tpls = available_rel_tpls
49 self.available_rel_types = available_rel_types
50 self._relationships = {}
53 def relationships(self):
54 if not self._relationships:
55 requires = self.requirements
58 for r1, value in r.items():
59 explicit = self._get_explicit_relationship(r, value)
61 for key, value in explicit.items():
62 self._relationships[key] = value
63 return self._relationships
65 def _get_explicit_relationship(self, req, value):
66 """Handle explicit relationship
71 relationship: tosca.relationships.HostedOn
73 explicit_relation = {}
74 node = value.get('node') if isinstance(value, dict) else value
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))
86 if node not in self.templates:
87 ExceptionCollector.appendException(
88 KeyError(_('Node template "%s" was not found.') % node))
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
96 parent_reqs = self.type_definition.get_all_requirements()
97 if parent_reqs is None:
98 ExceptionCollector.appendException(
99 ValidationError(message='parent_req is ' +
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).
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,
116 explicit_relation[rtype] = related_tpl
117 tpl.target = related_tpl
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')
127 if self.available_rel_types and \
128 relationship in self.available_rel_types.keys():
130 elif not relationship.startswith(rel_prfx):
131 relationship = rel_prfx + relationship
133 ExceptionCollector.appendException(
134 MissingRequiredFieldError(
135 what=_('"relationship" used in template '
136 '"%s"') % related_tpl.name,
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,
144 elif self.available_rel_types:
145 if relationship in self.available_rel_types.keys():
146 rel_type_def = self.available_rel_types.\
148 if 'derived_from' in rel_type_def:
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
156 _add_relationship_template(
157 req, rtype.type, self)
158 return explicit_relation
160 def _add_relationship_template(self, requirement, rtype, source):
161 req = requirement.copy()
163 tpl = RelationshipTemplate(req, rtype, self.custom_def, self, source)
164 self.relationship_tpl.append(tpl)
166 def get_relationship_template(self):
167 return self.relationship_tpl
169 def _add_next(self, nodetpl, relationship):
170 self.related[nodetpl] = relationship
173 def related_nodes(self):
175 for relation, node in self.type_definition.relationship.items():
176 for tpl in self.templates:
178 self.related[NodeTemplate(tpl)] = relation
179 return self.related.keys()
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():
189 def _validate_requirements(self):
190 type_requires = self.type_definition.get_all_requirements()
191 allowed_reqs = ["template"]
193 for treq in type_requires:
194 for key, value in treq.items():
195 allowed_reqs.append(key)
196 if isinstance(value, dict):
198 allowed_reqs.append(key)
200 requires = self.type_definition.get_value(self.REQUIREMENTS,
203 if not isinstance(requires, list):
204 ExceptionCollector.appendException(
206 what='"requirements" of template "%s"' % self.name,
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')
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)
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)))
235 def _validate_requirements_keys(self, requirement):
236 for key in requirement.keys():
237 if key not in self.REQUIREMENTS_SECTION:
238 ExceptionCollector.appendException(
240 what='"requirements" of template "%s"' % self.name,
243 def _validate_interfaces(self):
244 ifaces = self.type_definition.get_value(self.INTERFACES,
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,
254 elif name in (CONFIGURE, CONFIGURE_SHORTNAME):
255 self._common_validate_field(
256 value, InterfacesDef.
257 interfaces_relationship_configure_operations,
260 ExceptionCollector.appendException(
262 what='"interfaces" of template "%s"' %
263 self.name, field=name))
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,