Merge "Update tosca lib to version 0.5"
[parser.git] / tosca2heat / tosca-parser / toscaparser / elements / nodetype.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 from toscaparser.common.exception import ExceptionCollector
14 from toscaparser.common.exception import UnknownFieldError
15 from toscaparser.common.exception import ValidationError
16 from toscaparser.elements.capabilitytype import CapabilityTypeDef
17 import toscaparser.elements.interfaces as ifaces
18 from toscaparser.elements.interfaces import InterfacesDef
19 from toscaparser.elements.relationshiptype import RelationshipType
20 from toscaparser.elements.statefulentitytype import StatefulEntityType
21
22
23 class NodeType(StatefulEntityType):
24     '''TOSCA built-in node type.'''
25     SECTIONS = (DERIVED_FROM, METADATA, PROPERTIES, VERSION, DESCRIPTION, ATTRIBUTES, REQUIREMENTS, CAPABILITIES, INTERFACES, ARTIFACTS) = \
26                ('derived_from', 'metadata', 'properties', 'version',
27                 'description', 'attributes', 'requirements', 'capabilities',
28                 'interfaces', 'artifacts')
29
30     def __init__(self, ntype, custom_def=None):
31         super(NodeType, self).__init__(ntype, self.NODE_PREFIX, custom_def)
32         self.ntype = ntype
33         self.custom_def = custom_def
34         self._validate_keys()
35
36     @property
37     def parent_type(self):
38         '''Return a node this node is derived from.'''
39         if not hasattr(self, 'defs'):
40             return None
41         pnode = self.derived_from(self.defs)
42         if pnode:
43             return NodeType(pnode, self.custom_def)
44
45     @property
46     def relationship(self):
47         '''Return a dictionary of relationships to other node types.
48
49         This method returns a dictionary of named relationships that nodes
50         of the current node type (self) can have to other nodes (of specific
51         types) in a TOSCA template.
52
53         '''
54         relationship = {}
55         requires = self.get_all_requirements()
56         if requires:
57             # NOTE(sdmonov): Check if requires is a dict.
58             # If it is a dict convert it to a list of dicts.
59             # This is needed because currently the code below supports only
60             # lists as requirements definition. The following check will
61             # make sure if a map (dict) was provided it will be converted to
62             # a list before proceeding to the parsing.
63             if isinstance(requires, dict):
64                 requires = [{key: value} for key, value in requires.items()]
65
66             keyword = None
67             node_type = None
68             for require in requires:
69                 for key, req in require.items():
70                     if 'relationship' in req:
71                         relation = req.get('relationship')
72                         if 'type' in relation:
73                             relation = relation.get('type')
74                         node_type = req.get('node')
75                         value = req
76                         if node_type:
77                             keyword = 'node'
78                         else:
79                             # If value is a dict and has a type key
80                             # we need to lookup the node type using
81                             # the capability type
82                             value = req
83                             if isinstance(value, dict):
84                                 captype = value['capability']
85                                 value = (self.
86                                          _get_node_type_by_cap(key, captype))
87                             relation = self._get_relation(key, value)
88                             keyword = key
89                             node_type = value
90                 rtype = RelationshipType(relation, keyword, self.custom_def)
91                 relatednode = NodeType(node_type, self.custom_def)
92                 relationship[rtype] = relatednode
93         return relationship
94
95     def _get_node_type_by_cap(self, key, cap):
96         '''Find the node type that has the provided capability
97
98         This method will lookup all node types if they have the
99         provided capability.
100         '''
101
102         # Filter the node types
103         node_types = [node_type for node_type in self.TOSCA_DEF.keys()
104                       if node_type.startswith(self.NODE_PREFIX) and
105                       node_type != 'tosca.nodes.Root']
106
107         for node_type in node_types:
108             node_def = self.TOSCA_DEF[node_type]
109             if isinstance(node_def, dict) and 'capabilities' in node_def:
110                 node_caps = node_def['capabilities']
111                 for value in node_caps.values():
112                     if isinstance(value, dict) and \
113                             'type' in value and value['type'] == cap:
114                         return node_type
115
116     def _get_relation(self, key, ndtype):
117         relation = None
118         ntype = NodeType(ndtype)
119         caps = ntype.get_capabilities()
120         if caps and key in caps.keys():
121             c = caps[key]
122             for r in self.RELATIONSHIP_TYPE:
123                 rtypedef = ntype.TOSCA_DEF[r]
124                 for properties in rtypedef.values():
125                     if c.type in properties:
126                         relation = r
127                         break
128                 if relation:
129                     break
130                 else:
131                     for properties in rtypedef.values():
132                         if c.parent_type in properties:
133                             relation = r
134                             break
135         return relation
136
137     def get_capabilities_objects(self):
138         '''Return a list of capability objects.'''
139         typecapabilities = []
140         caps = self.get_value(self.CAPABILITIES, None, True)
141         if caps is None:
142             caps = self.get_value(self.CAPABILITIES, None, True)
143         if caps:
144             # 'name' is symbolic name of the capability
145             # 'value' is a dict { 'type': <capability type name> }
146             for name, value in caps.items():
147                 ctype = value.get('type')
148                 cap = CapabilityTypeDef(name, ctype, self.type,
149                                         self.custom_def)
150                 typecapabilities.append(cap)
151         return typecapabilities
152
153     def get_capabilities(self):
154         '''Return a dictionary of capability name-objects pairs.'''
155         return {cap.name: cap
156                 for cap in self.get_capabilities_objects()}
157
158     @property
159     def requirements(self):
160         return self.get_value(self.REQUIREMENTS, None, True)
161
162     def get_all_requirements(self):
163         requires = self.requirements
164         parent_node = self.parent_type
165         if requires is None:
166             requires = self.get_value(self.REQUIREMENTS, None, True)
167             if parent_node is None:
168                 ExceptionCollector.appendException(
169                     ValidationError(message="parent_node is "
170                                     + str(parent_node)))
171             else:
172                 parent_node = parent_node.parent_type
173         if parent_node:
174             while parent_node.type != 'tosca.nodes.Root':
175                 req = parent_node.get_value(self.REQUIREMENTS, None, True)
176                 for r in req:
177                     if r not in requires:
178                         requires.append(r)
179                 parent_node = parent_node.parent_type
180         return requires
181
182     @property
183     def interfaces(self):
184         return self.get_value(self.INTERFACES)
185
186     @property
187     def lifecycle_inputs(self):
188         '''Return inputs to life cycle operations if found.'''
189         inputs = []
190         interfaces = self.interfaces
191         if interfaces:
192             for name, value in interfaces.items():
193                 if name == ifaces.LIFECYCLE:
194                     for x, y in value.items():
195                         if x == 'inputs':
196                             for i in y.iterkeys():
197                                 inputs.append(i)
198         return inputs
199
200     @property
201     def lifecycle_operations(self):
202         '''Return available life cycle operations if found.'''
203         ops = None
204         interfaces = self.interfaces
205         if interfaces:
206             i = InterfacesDef(self.type, ifaces.LIFECYCLE)
207             ops = i.lifecycle_ops
208         return ops
209
210     def get_capability(self, name):
211         caps = self.get_capabilities()
212         if caps and name in caps.keys():
213             return caps[name].value
214
215     def get_capability_type(self, name):
216         captype = self.get_capability(name)
217         if captype and name in captype.keys():
218             return captype[name].value
219
220     def _validate_keys(self):
221         if self.defs:
222             for key in self.defs.keys():
223                 if key not in self.SECTIONS:
224                     ExceptionCollector.appendException(
225                         UnknownFieldError(what='Nodetype"%s"' % self.ntype,
226                                           field=key))