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