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