still works in unfederated context, with a bug fix:
[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.Sites import Site, Sites
9 from PLC.Nodes import Node, Nodes
10 from PLC.NodeNetworks import NodeNetwork, NodeNetworks
11 from PLC.NodeGroups import NodeGroup, NodeGroups
12 from PLC.ConfFiles import ConfFile, ConfFiles
13 from PLC.Slices import Slice, Slices
14 from PLC.ForeignSlices import ForeignSlice, ForeignSlices
15 from PLC.Persons import Person, Persons
16 from PLC.Keys import Key, Keys
17 from PLC.SliceAttributes import SliceAttribute, SliceAttributes
18
19 class GetSlivers(Method):
20     """
21     Returns an array of structs representing nodes and their slivers
22     (slices bound to nodes). If node_filter is specified, only
23     information about the specified nodes will be returned. If
24     node_filter is not specified and called by a node, only
25     information about the caller will be returned.
26
27     All of the information returned by this call can be gathered from
28     other calls, e.g. GetNodes, GetNodeNetworks, GetSlices, etc. This
29     function exists primarily for the benefit of Node Manager and
30     Federation Manager.
31     """
32
33     roles = ['admin', 'node']
34
35     accepts = [
36         Auth(),
37         Mixed([Mixed(Node.fields['node_id'],
38                      Node.fields['hostname'])],
39               Filter(Node.fields)),
40         ]
41
42     returns = [{
43         'timestamp': Parameter(int, "Timestamp of this call, in seconds since UNIX epoch"),
44         'node_id': Node.fields['node_id'],
45         'hostname': Node.fields['hostname'],
46         'networks': [NodeNetwork.fields],
47         'groups': [NodeGroup.fields['name']],
48         'conf_files': [ConfFile.fields],
49         'slivers': [{
50             'name': Slice.fields['name'],
51             'slice_id': Slice.fields['slice_id'],
52             'instantiation': Slice.fields['instantiation'],
53             'expires': Slice.fields['expires'],
54             'keys': [{
55                 'key_type': Key.fields['key_type'],
56                 'key': Key.fields['key']
57             }],
58             'attributes': [{
59                 'name': SliceAttribute.fields['name'],
60                 'value': SliceAttribute.fields['value']
61             }]
62         }]
63     }]
64
65     def call(self, auth, node_filter = None):
66         timestamp = int(time.time())
67
68         if node_filter is None and isinstance(self.caller, Node):
69             all_nodes = {self.caller['node_id']: self.caller}
70         else:
71             all_nodes = Nodes(self.api, node_filter).dict()
72             # XXX Add foreign nodes
73
74         # Get default slices
75         default_slice_ids = set()
76         sites = Sites(self.api, [self.api.config.PLC_SLICE_PREFIX])
77         if sites:
78             default_slice_ids.update(sites[0]['slice_ids'])
79
80         nodenetwork_ids = set()
81         nodegroup_ids = set()
82         # Thierry : need to copy here otherwise side effects
83         # on slice_ids would affect default_slice_ids,
84         # that we still need later on
85         slice_ids = set(default_slice_ids)
86         for node_id, node in all_nodes.iteritems():
87             nodenetwork_ids.update(node['nodenetwork_ids'])
88             nodegroup_ids.update(node['nodegroup_ids'])
89             slice_ids.update(node['slice_ids'])
90
91         # Get nodenetwork information
92         all_nodenetworks = NodeNetworks(self.api, nodenetwork_ids).dict()
93
94         # Get node group information
95         all_nodegroups = NodeGroups(self.api, nodegroup_ids).dict()
96
97         # Get (enabled) configuration files
98         all_conf_files = ConfFiles(self.api, {'enabled': True}).dict()
99
100         # Get slice information
101         # hacky by now - merge slices and foreign slices 
102         all_slices = Slices(self.api, slice_ids).dict()
103         foreign_slices_list = ForeignSlices (self.api, slice_ids)
104         for foreign_slice in foreign_slices_list:
105             all_slices[foreign_slice['slice_id']]=foreign_slice
106
107         person_ids = set()
108         slice_attribute_ids = set()
109         for slice_id, slice in all_slices.iteritems():
110             ### still missing in foreign slices
111             if slice.get('person_ids'):
112                 person_ids.update(slice['person_ids'])
113             ### still missing in foreign slices
114             if slice.get('slice_attribute_ids'):
115                 slice_attribute_ids.update(slice['slice_attribute_ids'])
116
117         # Get user information
118         all_persons = Persons(self.api, person_ids).dict()
119
120         key_ids = set()
121         for person_id, person in all_persons.iteritems():
122             key_ids.update(person['key_ids'])
123
124         # Get user account keys
125         all_keys = Keys(self.api, key_ids).dict()
126
127         # Get slice attributes
128         all_slice_attributes = SliceAttributes(self.api, slice_attribute_ids).dict()
129
130         nodes = []
131         for node_id, node in all_nodes.iteritems():
132             networks = [all_nodenetworks[nodenetwork_id] for nodenetwork_id in node['nodenetwork_ids']]
133             nodegroups = [all_nodegroups[nodegroup_id] for nodegroup_id in node['nodegroup_ids']]
134             groups = [nodegroup['name'] for nodegroup in nodegroups]
135
136             # If multiple entries for the same global configuration
137             # file exist, it is undefined which one takes precedence.
138             conf_files = {}
139             for conf_file in all_conf_files.values():
140                 if not conf_file['node_ids'] and not conf_file['nodegroup_ids']:
141                     conf_files[conf_file['dest']] = conf_file
142
143             # If a node belongs to multiple node
144             # groups for which the same configuration file is defined,
145             # it is undefined which one takes precedence.
146             for nodegroup in nodegroups:
147                 for conf_file_id in nodegroup['conf_file_ids']:
148                     if conf_file_id in all_conf_files:
149                         conf_files[conf_file['dest']] = all_conf_files[conf_file_id]
150
151             # Node configuration files always take precedence over
152             # node group configuration files.
153             for conf_file_id in node['conf_file_ids']:
154                 if conf_file_id in all_conf_files:
155                     conf_files[conf_file['dest']] = all_conf_files[conf_file_id]
156
157             slice_ids = node['slice_ids']
158
159             # If not a foreign node, add all of our default system
160             # slices to it.
161             # Thierry : foreign nodes are totally out of scope
162             # we dont know how to answer GetSlivers for foreign nodes.
163             slice_ids += default_slice_ids
164
165
166             slivers = []
167             for slice in map(lambda id: all_slices[id], slice_ids):
168                 keys = []
169                 ### still missing in foreign slices
170                 try:
171                     for person in map(lambda id: all_persons[id], slice['person_ids']):
172                         keys += [{'key_type': all_keys[key_id]['key_type'],
173                                   'key': all_keys[key_id]['key']} \
174                                  for key_id in person['key_ids']]
175                 except:
176                     keys += [{'key_type':'missing',
177                               'key':'key caching not implemented yet'}]
178
179                 attributes = {}
180                 ### still missing in foreign slices
181                 try:
182                     for slice_attribute in map(lambda id: all_slice_attributes[id],
183                                                slice['slice_attribute_ids']):
184                         # Per-node sliver attributes (slice attributes
185                         # with non-null node_id fields) take precedence
186                         # over global slice attributes.
187                         if not attributes.has_key(slice_attribute['name']) or \
188                                slice_attribute['node_id'] is not None:
189                             attributes[slice_attribute['name']] = {
190                                 'name': slice_attribute['name'],
191                                 'value': slice_attribute['value']
192                                 }
193                 except:
194                         attributes={'ignored':{'name':'attributes caching','value':'not implemented yet'}}
195
196                 slivers.append({
197                     'name': slice['name'],
198                     'slice_id': slice['slice_id'],
199                     'instantiation': slice['instantiation'],
200                     'expires': slice['expires'],
201                     'keys': keys,
202                     'attributes': attributes.values()
203                     })
204
205             nodes.append({
206                 'timestamp': timestamp,
207                 'node_id': node['node_id'],
208                 'hostname': node['hostname'],
209                 'networks': networks,
210                 'groups': groups,
211                 'conf_files': conf_files.values(),
212                 'slivers': slivers
213                 })
214
215         return nodes