step2 : basic functions for handling nodetags and nodegroups - still highly volatile
[plcapi.git] / PLC / Methods / GetSlivers.py
1 import time
2
3 from PLC.Faults import *
4 from PLC.Method import Method
5 from PLC.Parameter import Parameter, Mixed
6 from PLC.Filter import Filter
7 from PLC.Auth import Auth
8 from PLC.Nodes import Node, Nodes
9 from PLC.Interfaces import Interface, Interfaces
10 from PLC.NodeGroups import NodeGroup, NodeGroups
11 from PLC.ConfFiles import ConfFile, ConfFiles
12 from PLC.Slices import Slice, Slices
13 from PLC.Persons import Person, Persons
14 from PLC.Keys import Key, Keys
15 from PLC.SliceAttributes import SliceAttribute, SliceAttributes
16 from PLC.InitScripts import InitScript, InitScripts
17
18 def get_slivers(api, slice_filter, node = None):
19     # Get slice information
20     slices = Slices(api, slice_filter, ['slice_id', 'name', 'instantiation', 'expires', 'person_ids', 'slice_attribute_ids'])
21
22     # Build up list of users and slice attributes
23     person_ids = set()
24     slice_attribute_ids = set()
25     for slice in slices:
26         person_ids.update(slice['person_ids'])
27         slice_attribute_ids.update(slice['slice_attribute_ids'])
28
29     # Get user information
30     all_persons = Persons(api, {'person_id':person_ids,'enabled':True}, ['person_id', 'enabled', 'key_ids']).dict()
31
32     # Build up list of keys
33     key_ids = set()
34     for person in all_persons.values():
35         key_ids.update(person['key_ids'])
36
37     # Get user account keys
38     all_keys = Keys(api, key_ids, ['key_id', 'key', 'key_type']).dict()
39
40     # Get slice attributes
41     all_slice_attributes = SliceAttributes(api, slice_attribute_ids).dict()
42
43     slivers = []
44     for slice in slices:
45         keys = []
46         for person_id in slice['person_ids']:
47             if person_id in all_persons:
48                 person = all_persons[person_id]
49                 if not person['enabled']:
50                     continue
51                 for key_id in person['key_ids']:
52                     if key_id in all_keys:
53                         key = all_keys[key_id]
54                         keys += [{'key_type': key['key_type'],
55                                   'key': key['key']}]
56
57         attributes = []
58
59         # All (per-node and global) attributes for this slice
60         slice_attributes = []
61         for slice_attribute_id in slice['slice_attribute_ids']:
62             if slice_attribute_id in all_slice_attributes:
63                 slice_attributes.append(all_slice_attributes[slice_attribute_id])
64
65         # Per-node sliver attributes take precedence over global
66         # slice attributes, so set them first.
67         # Then comes nodegroup slice attributes
68         # Followed by global slice attributes
69         sliver_attributes = []
70
71         if node is not None:
72             for sliver_attribute in filter(lambda a: a['node_id'] == node['node_id'], slice_attributes):
73                 sliver_attributes.append(sliver_attribute['name'])
74                 attributes.append({'name': sliver_attribute['name'],
75                                    'value': sliver_attribute['value']})
76
77             # set nodegroup slice attributes
78             for slice_attribute in filter(lambda a: a['nodegroup_id'] in node['nodegroup_ids'], slice_attributes):
79                 # Do not set any nodegroup slice attributes for
80                 # which there is at least one sliver attribute
81                 # already set.
82                 if slice_attribute['name'] not in slice_attributes:
83                     attributes.append({'name': slice_attribute['name'],
84                                    'value': slice_attribute['value']})
85
86         for slice_attribute in filter(lambda a: a['node_id'] is None, slice_attributes):
87             # Do not set any global slice attributes for
88             # which there is at least one sliver attribute
89             # already set.
90             if slice_attribute['name'] not in sliver_attributes:
91                 attributes.append({'name': slice_attribute['name'],
92                                    'value': slice_attribute['value']})
93
94         slivers.append({
95             'name': slice['name'],
96             'slice_id': slice['slice_id'],
97             'instantiation': slice['instantiation'],
98             'expires': slice['expires'],
99             'keys': keys,
100             'attributes': attributes
101             })
102
103     return slivers
104
105 class GetSlivers(Method):
106     """
107     Returns a struct containing information about the specified node
108     (or calling node, if called by a node and node_id_or_hostname is
109     not specified), including the current set of slivers bound to the
110     node.
111
112     All of the information returned by this call can be gathered from
113     other calls, e.g. GetNodes, GetInterfaces, GetSlices, etc. This
114     function exists almost solely for the benefit of Node Manager.
115     """
116
117     roles = ['admin', 'node']
118
119     accepts = [
120         Auth(),
121         Mixed(Node.fields['node_id'],
122               Node.fields['hostname']),
123         ]
124
125     returns = {
126         'timestamp': Parameter(int, "Timestamp of this call, in seconds since UNIX epoch"),
127         'node_id': Node.fields['node_id'],
128         'hostname': Node.fields['hostname'],
129         'networks': [Interface.fields],
130         'groups': [NodeGroup.fields['groupname']],
131         'conf_files': [ConfFile.fields],
132         'initscripts': [InitScript.fields],
133         'slivers': [{
134             'name': Slice.fields['name'],
135             'slice_id': Slice.fields['slice_id'],
136             'instantiation': Slice.fields['instantiation'],
137             'expires': Slice.fields['expires'],
138             'keys': [{
139                 'key_type': Key.fields['key_type'],
140                 'key': Key.fields['key']
141             }],
142             'attributes': [{
143                 'name': SliceAttribute.fields['name'],
144                 'value': SliceAttribute.fields['value']
145             }]
146         }]
147     }
148
149     def call(self, auth, node_id_or_hostname = None):
150         timestamp = int(time.time())
151
152         # Get node
153         if node_id_or_hostname is None:
154             if isinstance(self.caller, Node):
155                 node = self.caller
156             else:
157                 raise PLCInvalidArgument, "'node_id_or_hostname' not specified"
158         else:
159             nodes = Nodes(self.api, [node_id_or_hostname])
160             if not nodes:
161                 raise PLCInvalidArgument, "No such node"
162             node = nodes[0]
163
164             if node['peer_id'] is not None:
165                 raise PLCInvalidArgument, "Not a local node"
166
167         # Get interface information
168         networks = Interfaces(self.api, node['interface_ids'])
169
170         # Get node group information
171         nodegroups = NodeGroups(self.api, node['nodegroup_ids']).dict('name')
172         groups = nodegroups.keys()
173
174         # Get all (enabled) configuration files
175         all_conf_files = ConfFiles(self.api, {'enabled': True}).dict()
176         conf_files = {}
177
178         # Global configuration files are the default. If multiple
179         # entries for the same global configuration file exist, it is
180         # undefined which one takes precedence.
181         for conf_file in all_conf_files.values():
182             if not conf_file['node_ids'] and not conf_file['nodegroup_ids']:
183                 conf_files[conf_file['dest']] = conf_file
184         
185         # Node group configuration files take precedence over global
186         # ones. If a node belongs to multiple node groups for which
187         # the same configuration file is defined, it is undefined
188         # which one takes precedence.
189         for nodegroup in nodegroups.values():
190             for conf_file_id in nodegroup['conf_file_ids']:
191                 if conf_file_id in all_conf_files:
192                     conf_file = all_conf_files[conf_file_id]
193                     conf_files[conf_file['dest']] = conf_file
194         
195         # Node configuration files take precedence over node group
196         # configuration files.
197         for conf_file_id in node['conf_file_ids']:
198             if conf_file_id in all_conf_files:
199                 conf_file = all_conf_files[conf_file_id]
200                 conf_files[conf_file['dest']] = conf_file            
201
202         # Get all (enabled) initscripts
203         initscripts = InitScripts(self.api, {'enabled': True})  
204
205         # Get system slices
206         system_slice_attributes = SliceAttributes(self.api, {'name': 'system', 'value': '1'}).dict('slice_id')
207         system_slice_ids = system_slice_attributes.keys()
208         
209         # Get nm-controller slices
210         controller_and_delegated_slices = Slices(self.api, {'instantiation': ['nm-controller', 'delegated']}, ['slice_id']).dict('slice_id')
211         controller_and_delegated_slice_ids = controller_and_delegated_slices.keys()
212         slice_ids = system_slice_ids + controller_and_delegated_slice_ids + node['slice_ids']
213
214         slivers = get_slivers(self.api, slice_ids, node)
215
216         node.update_last_contact()
217
218         return {
219             'timestamp': timestamp,
220             'node_id': node['node_id'],
221             'hostname': node['hostname'],
222             'networks': networks,
223             'groups': groups,
224             'conf_files': conf_files.values(),
225             'initscripts': initscripts,
226             'slivers': slivers
227             }