Add input validation in substitution_mapping class
[parser.git] / tosca2heat / tosca-parser / toscaparser / topology_template.py
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
4 #
5 #         http://www.apache.org/licenses/LICENSE-2.0
6 #
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
11 #    under the License.
12
13
14 import logging
15
16 from toscaparser.common import exception
17 from toscaparser.dataentity import DataEntity
18 from toscaparser import functions
19 from toscaparser.groups import Group
20 from toscaparser.nodetemplate import NodeTemplate
21 from toscaparser.parameters import Input
22 from toscaparser.parameters import Output
23 from toscaparser.policy import Policy
24 from toscaparser.relationship_template import RelationshipTemplate
25 from toscaparser.substitution_mappings import SubstitutionMappings
26 from toscaparser.tpl_relationship_graph import ToscaGraph
27 from toscaparser.utils.gettextutils import _
28
29
30 # Topology template key names
31 SECTIONS = (DESCRIPTION, INPUTS, NODE_TEMPLATES,
32             RELATIONSHIP_TEMPLATES, OUTPUTS, GROUPS,
33             SUBSTITUION_MAPPINGS, POLICIES) = \
34            ('description', 'inputs', 'node_templates',
35             'relationship_templates', 'outputs', 'groups',
36             'substitution_mappings', 'policies')
37
38 log = logging.getLogger("tosca.model")
39
40
41 class TopologyTemplate(object):
42
43     '''Load the template data.'''
44     def __init__(self, template, custom_defs,
45                  rel_types=None, parsed_params=None,
46                  sub_mapped_node_template=None):
47         self.tpl = template
48         self.sub_mapped_node_template = sub_mapped_node_template
49         if self.tpl:
50             self.custom_defs = custom_defs
51             self.rel_types = rel_types
52             self.parsed_params = parsed_params
53             self._validate_field()
54             self.description = self._tpl_description()
55             self.inputs = self._inputs()
56             self.relationship_templates = self._relationship_templates()
57             self.nodetemplates = self._nodetemplates()
58             self.outputs = self._outputs()
59             if hasattr(self, 'nodetemplates'):
60                 self.graph = ToscaGraph(self.nodetemplates)
61             self.groups = self._groups()
62             self.policies = self._policies()
63             self._process_intrinsic_functions()
64             self.substitution_mappings = self._substitution_mappings()
65
66     def _inputs(self):
67         inputs = []
68         for name, attrs in self._tpl_inputs().items():
69             input = Input(name, attrs)
70             if self.parsed_params and name in self.parsed_params:
71                 input.validate(self.parsed_params[name])
72             else:
73                 default = input.default
74                 if default:
75                     input.validate(default)
76             inputs.append(input)
77         return inputs
78
79     def _nodetemplates(self):
80         nodetemplates = []
81         tpls = self._tpl_nodetemplates()
82         if tpls:
83             for name in tpls:
84                 tpl = NodeTemplate(name, tpls, self.custom_defs,
85                                    self.relationship_templates,
86                                    self.rel_types)
87                 if (tpl.type_definition and
88                     (tpl.type in tpl.type_definition.TOSCA_DEF or
89                      (tpl.type not in tpl.type_definition.TOSCA_DEF and
90                       bool(tpl.custom_def)))):
91                     tpl.validate(self)
92                     nodetemplates.append(tpl)
93         return nodetemplates
94
95     def _relationship_templates(self):
96         rel_templates = []
97         tpls = self._tpl_relationship_templates()
98         for name in tpls:
99             tpl = RelationshipTemplate(tpls[name], name, self.custom_defs)
100             rel_templates.append(tpl)
101         return rel_templates
102
103     def _outputs(self):
104         outputs = []
105         for name, attrs in self._tpl_outputs().items():
106             output = Output(name, attrs)
107             output.validate()
108             outputs.append(output)
109         return outputs
110
111     def _substitution_mappings(self):
112         tpl_substitution_mapping = self._tpl_substitution_mappings()
113         if tpl_substitution_mapping:
114             return SubstitutionMappings(tpl_substitution_mapping,
115                                         self.nodetemplates,
116                                         self.inputs,
117                                         self.outputs,
118                                         self.sub_mapped_node_template,
119                                         self.custom_defs)
120
121     def _policies(self):
122         policies = []
123         for policy in self._tpl_policies():
124             for policy_name, policy_tpl in policy.items():
125                 target_list = policy_tpl.get('targets')
126                 if target_list and len(target_list) >= 1:
127                     target_objects = []
128                     targets_type = "groups"
129                     target_objects = self._get_policy_groups(target_list)
130                     if not target_objects:
131                         targets_type = "node_templates"
132                         target_objects = self._get_group_members(target_list)
133                     policyObj = Policy(policy_name, policy_tpl,
134                                        target_objects, targets_type,
135                                        self.custom_defs)
136                     policies.append(policyObj)
137         return policies
138
139     def _groups(self):
140         groups = []
141         member_nodes = None
142         for group_name, group_tpl in self._tpl_groups().items():
143             member_names = group_tpl.get('members')
144             if member_names is not None:
145                 DataEntity.validate_datatype('list', member_names)
146                 if len(member_names) < 1 or \
147                     len(member_names) != len(set(member_names)):
148                     exception.ExceptionCollector.appendException(
149                         exception.InvalidGroupTargetException(
150                             message=_('Member nodes "%s" should be >= 1 '
151                                       'and not repeated') % member_names))
152                 else:
153                     member_nodes = self._get_group_members(member_names)
154             group = Group(group_name, group_tpl,
155                           member_nodes,
156                           self.custom_defs)
157             groups.append(group)
158         return groups
159
160     def _get_group_members(self, member_names):
161         member_nodes = []
162         self._validate_group_members(member_names)
163         for member in member_names:
164             for node in self.nodetemplates:
165                 if node.name == member:
166                     member_nodes.append(node)
167         return member_nodes
168
169     def _get_policy_groups(self, member_names):
170         member_groups = []
171         for member in member_names:
172             for group in self.groups:
173                 if group.name == member:
174                     member_groups.append(group)
175         return member_groups
176
177     def _validate_group_members(self, members):
178         node_names = []
179         for node in self.nodetemplates:
180             node_names.append(node.name)
181         for member in members:
182             if member not in node_names:
183                 exception.ExceptionCollector.appendException(
184                     exception.InvalidGroupTargetException(
185                         message=_('Target member "%s" is not found in '
186                                   'node_templates') % member))
187
188     # topology template can act like node template
189     # it is exposed by substitution_mappings.
190     def nodetype(self):
191         return (self.substitution_mappings.node_type
192                 if self.substitution_mappings else None)
193
194     def capabilities(self):
195         return (self.substitution_mappings.capabilities
196                 if self.substitution_mappings else None)
197
198     def requirements(self):
199         return (self.substitution_mappings.requirements
200                 if self.substitution_mappings else None)
201
202     def _tpl_description(self):
203         description = self.tpl.get(DESCRIPTION)
204         if description:
205             return description.rstrip()
206
207     def _tpl_inputs(self):
208         return self.tpl.get(INPUTS) or {}
209
210     def _tpl_nodetemplates(self):
211         return self.tpl.get(NODE_TEMPLATES)
212
213     def _tpl_relationship_templates(self):
214         return self.tpl.get(RELATIONSHIP_TEMPLATES) or {}
215
216     def _tpl_outputs(self):
217         return self.tpl.get(OUTPUTS) or {}
218
219     def _tpl_substitution_mappings(self):
220         return self.tpl.get(SUBSTITUION_MAPPINGS) or {}
221
222     def _tpl_groups(self):
223         return self.tpl.get(GROUPS) or {}
224
225     def _tpl_policies(self):
226         return self.tpl.get(POLICIES) or {}
227
228     def _validate_field(self):
229         for name in self.tpl:
230             if name not in SECTIONS:
231                 exception.ExceptionCollector.appendException(
232                     exception.UnknownFieldError(what='Template', field=name))
233
234     def _process_intrinsic_functions(self):
235         """Process intrinsic functions
236
237         Current implementation processes functions within node template
238         properties, requirements, interfaces inputs and template outputs.
239         """
240         if hasattr(self, 'nodetemplates'):
241             for node_template in self.nodetemplates:
242                 for prop in node_template.get_properties_objects():
243                     prop.value = functions.get_function(self,
244                                                         node_template,
245                                                         prop.value)
246                 for interface in node_template.interfaces:
247                     if interface.inputs:
248                         for name, value in interface.inputs.items():
249                             interface.inputs[name] = functions.get_function(
250                                 self,
251                                 node_template,
252                                 value)
253                 if node_template.requirements:
254                     for req in node_template.requirements:
255                         rel = req
256                         for req_name, req_item in req.items():
257                             if isinstance(req_item, dict):
258                                 rel = req_item.get('relationship')
259                                 break
260                         if rel and 'properties' in rel:
261                             for key, value in rel['properties'].items():
262                                 rel['properties'][key] = \
263                                     functions.get_function(self,
264                                                            req,
265                                                            value)
266                 if node_template.get_capabilities_objects():
267                     for cap in node_template.get_capabilities_objects():
268                         if cap.get_properties_objects():
269                             for prop in cap.get_properties_objects():
270                                 propvalue = functions.get_function(
271                                     self,
272                                     node_template,
273                                     prop.value)
274                                 if isinstance(propvalue, functions.GetInput):
275                                     propvalue = propvalue.result()
276                                     for p, v in cap._properties.items():
277                                         if p == prop.name:
278                                             cap._properties[p] = propvalue
279                 for rel, node in node_template.relationships.items():
280                     rel_tpls = node.relationship_tpl
281                     if rel_tpls:
282                         for rel_tpl in rel_tpls:
283                             for interface in rel_tpl.interfaces:
284                                 if interface.inputs:
285                                     for name, value in \
286                                         interface.inputs.items():
287                                         interface.inputs[name] = \
288                                             functions.get_function(self,
289                                                                    rel_tpl,
290                                                                    value)
291         for output in self.outputs:
292             func = functions.get_function(self, self.outputs, output.value)
293             if isinstance(func, functions.GetAttribute):
294                 output.attrs[output.VALUE] = func
295
296     @classmethod
297     def get_sub_mapping_node_type(cls, topology_tpl):
298         if topology_tpl and isinstance(topology_tpl, dict):
299             submap_tpl = topology_tpl.get(SUBSTITUION_MAPPINGS)
300             return SubstitutionMappings.get_node_type(submap_tpl)