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 = {}
51 self.substitution_mapped = None
54 def relationships(self):
55 if not self._relationships:
56 requires = self.requirements
59 for r1, value in r.items():
60 explicit = self._get_explicit_relationship(r, value)
62 for key, value in explicit.items():
63 self._relationships[key] = value
64 return self._relationships
66 def _get_explicit_relationship(self, req, value):
67 """Handle explicit relationship
72 relationship: tosca.relationships.HostedOn
74 explicit_relation = {}
75 node = value.get('node') if isinstance(value, dict) else value
78 # TODO(spzala) implement look up once Glance meta data is available
79 # to find a matching TOSCA node using the TOSCA types
80 msg = _('Lookup by TOSCA types is not supported. '
81 'Requirement for "%s" can not be full-filled.') % self.name
82 if (node in list(self.type_definition.TOSCA_DEF.keys())
83 or node in self.custom_def):
84 ExceptionCollector.appendException(NotImplementedError(msg))
87 if node not in self.templates:
88 ExceptionCollector.appendException(
89 KeyError(_('Node template "%s" was not found.') % node))
92 related_tpl = NodeTemplate(node, self.templates, self.custom_def)
93 relationship = value.get('relationship') \
94 if isinstance(value, dict) else None
95 # check if it's type has relationship defined
97 parent_reqs = self.type_definition.get_all_requirements()
98 if parent_reqs is None:
99 ExceptionCollector.appendException(
100 ValidationError(message='parent_req is ' +
103 for key in req.keys():
104 for req_dict in parent_reqs:
105 if key in req_dict.keys():
106 relationship = (req_dict.get(key).
110 found_relationship_tpl = False
111 # apply available relationship templates if found
112 if self.available_rel_tpls:
113 for tpl in self.available_rel_tpls:
114 if tpl.name == relationship:
115 rtype = RelationshipType(tpl.type, None,
117 explicit_relation[rtype] = related_tpl
118 tpl.target = related_tpl
120 self.relationship_tpl.append(tpl)
121 found_relationship_tpl = True
122 # create relationship template object.
123 rel_prfx = self.type_definition.RELATIONSHIP_PREFIX
124 if not found_relationship_tpl:
125 if isinstance(relationship, dict):
126 relationship = relationship.get('type')
128 if self.available_rel_types and \
129 relationship in self.available_rel_types.keys():
131 elif not relationship.startswith(rel_prfx):
132 relationship = rel_prfx + relationship
134 ExceptionCollector.appendException(
135 MissingRequiredFieldError(
136 what=_('"relationship" used in template '
137 '"%s"') % related_tpl.name,
139 for rtype in self.type_definition.relationship.keys():
140 if rtype.type == relationship:
141 explicit_relation[rtype] = related_tpl
142 related_tpl._add_relationship_template(req,
145 elif self.available_rel_types:
146 if relationship in self.available_rel_types.keys():
147 rel_type_def = self.available_rel_types.\
149 if 'derived_from' in rel_type_def:
151 rel_type_def.get('derived_from')
152 if not super_type.startswith(rel_prfx):
153 super_type = rel_prfx + super_type
154 if rtype.type == super_type:
155 explicit_relation[rtype] = related_tpl
157 _add_relationship_template(
158 req, rtype.type, self)
159 return explicit_relation
161 def _add_relationship_template(self, requirement, rtype, source):
162 req = requirement.copy()
164 tpl = RelationshipTemplate(req, rtype, self.custom_def, self, source)
165 self.relationship_tpl.append(tpl)
167 def get_relationship_template(self):
168 return self.relationship_tpl
170 def _add_next(self, nodetpl, relationship):
171 self.related[nodetpl] = relationship
174 def related_nodes(self):
176 for relation, node in self.type_definition.relationship.items():
177 for tpl in self.templates:
179 self.related[NodeTemplate(tpl)] = relation
180 return self.related.keys()
182 def validate(self, tosca_tpl=None):
183 self._validate_capabilities()
184 self._validate_requirements()
185 self._validate_properties(self.entity_tpl, self.type_definition)
186 self._validate_interfaces()
187 for prop in self.get_properties_objects():
190 def _validate_requirements(self):
191 type_requires = self.type_definition.get_all_requirements()
192 allowed_reqs = ["template"]
194 for treq in type_requires:
195 for key, value in treq.items():
196 allowed_reqs.append(key)
197 if isinstance(value, dict):
199 allowed_reqs.append(key)
201 requires = self.type_definition.get_value(self.REQUIREMENTS,
204 if not isinstance(requires, list):
205 ExceptionCollector.appendException(
207 what='"requirements" of template "%s"' % self.name,
210 for r1, value in req.items():
211 if isinstance(value, dict):
212 self._validate_requirements_keys(value)
213 self._validate_requirements_properties(value)
214 allowed_reqs.append(r1)
215 self._common_validate_field(req, allowed_reqs, 'requirements')
217 def _validate_requirements_properties(self, requirements):
218 # TODO(anyone): Only occurrences property of the requirements is
219 # validated here. Validation of other requirement properties are being
220 # validated in different files. Better to keep all the requirements
221 # properties validation here.
222 for key, value in requirements.items():
223 if key == 'occurrences':
224 self._validate_occurrences(value)
227 def _validate_occurrences(self, occurrences):
228 DataEntity.validate_datatype('list', occurrences)
229 for value in occurrences:
230 DataEntity.validate_datatype('integer', value)
231 if len(occurrences) != 2 or not (0 <= occurrences[0] <= occurrences[1]) \
232 or occurrences[1] == 0:
233 ExceptionCollector.appendException(
234 InvalidPropertyValueError(what=(occurrences)))
236 def _validate_requirements_keys(self, requirement):
237 for key in requirement.keys():
238 if key not in self.REQUIREMENTS_SECTION:
239 ExceptionCollector.appendException(
241 what='"requirements" of template "%s"' % self.name,
244 def _validate_interfaces(self):
245 ifaces = self.type_definition.get_value(self.INTERFACES,
249 for name, value in ifaces.items():
250 if name in (LIFECYCLE, LIFECYCLE_SHORTNAME):
251 self._common_validate_field(
252 value, InterfacesDef.
253 interfaces_node_lifecycle_operations,
255 elif name in (CONFIGURE, CONFIGURE_SHORTNAME):
256 self._common_validate_field(
257 value, InterfacesDef.
258 interfaces_relationship_configure_operations,
261 ExceptionCollector.appendException(
263 what='"interfaces" of template "%s"' %
264 self.name, field=name))
266 def _validate_fields(self, nodetemplate):
267 for name in nodetemplate.keys():
268 if name not in self.SECTIONS and name not in self.SPECIAL_SECTIONS:
269 ExceptionCollector.appendException(
270 UnknownFieldError(what='Node template "%s"' % self.name,