46e13d3c5ec856d7c2f8ccb16d420ea015617ff1
[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     event_type = 'Get'
65     object_type = 'Sliver'
66
67     def call(self, auth, node_filter = None):
68         timestamp = int(time.time())
69
70         if node_filter is None and isinstance(self.caller, Node):
71             all_nodes = {self.caller['node_id']: self.caller}
72         else:
73             all_nodes = Nodes(self.api, node_filter).dict()
74
75         # Get default slices
76         system_slice_attributes = SliceAttributes(self.api, {'name': 'system', 'value': '1'}).dict()
77         system_slice_ids = [slice_attribute['slice_id'] for slice_attribute in system_slice_attributes.values()]
78         system_slice_ids = dict.fromkeys(system_slice_ids)
79         
80         all_nodenetwork_ids = set()
81         all_nodegroup_ids = set()
82         all_slice_ids = set(system_slice_ids.keys())
83         for node_id, node in all_nodes.iteritems():
84             all_nodenetwork_ids.update(node['nodenetwork_ids'])
85             all_nodegroup_ids.update(node['nodegroup_ids'])
86             #all_slice_ids.update(node['slice_ids'])
87         
88         # Get nodenetwork information
89         all_nodenetworks = NodeNetworks(self.api, all_nodenetwork_ids).dict()
90
91         # Get node group information
92         all_nodegroups = NodeGroups(self.api, all_nodegroup_ids).dict()
93
94         # Get (enabled) configuration files
95         all_conf_files = ConfFiles(self.api, {'enabled': True}).dict()
96
97         # Get slice information
98         all_slices = Slices(self.api, all_slice_ids).dict()
99
100         person_ids = set()
101         slice_attribute_ids = set()
102         for slice_id, slice in all_slices.iteritems():
103             ### still missing in foreign slices
104             if slice.get('person_ids'):
105                 person_ids.update(slice['person_ids'])
106             ### still missing in foreign slices
107             if slice.get('slice_attribute_ids'):
108                 slice_attribute_ids.update(slice['slice_attribute_ids'])
109
110         # Get user information
111         all_persons = Persons(self.api, person_ids).dict()
112
113         key_ids = set()
114         for person_id, person in all_persons.iteritems():
115             key_ids.update(person['key_ids'])
116
117         # Get user account keys
118         all_keys = Keys(self.api, key_ids).dict()
119
120         # Get slice attributes
121         all_slice_attributes = SliceAttributes(self.api, slice_attribute_ids).dict()
122         
123         nodes = []
124         for node_id, node in all_nodes.iteritems():
125             networks = [all_nodenetworks[nodenetwork_id] for nodenetwork_id in node['nodenetwork_ids']]
126             nodegroups = [all_nodegroups[nodegroup_id] for nodegroup_id in node['nodegroup_ids']]
127             groups = [nodegroup['name'] for nodegroup in nodegroups]
128
129             # If multiple entries for the same global configuration
130             # file exist, it is undefined which one takes precedence.
131             conf_files = {}
132             for conf_file in all_conf_files.values():
133                 if not conf_file['node_ids'] and not conf_file['nodegroup_ids']:
134                     conf_files[conf_file['dest']] = conf_file
135             
136             # If a node belongs to multiple node
137             # groups for which the same configuration file is defined,
138             # it is undefined which one takes precedence.
139             for nodegroup in nodegroups:
140                 for conf_file_id in nodegroup['conf_file_ids']:
141                     if conf_file_id in all_conf_files:
142                         conf_files[conf_file['dest']] = all_conf_files[conf_file_id]
143             
144             # Node configuration files always take precedence over
145             # node group configuration files.
146             for conf_file_id in node['conf_file_ids']:
147                 if conf_file_id in all_conf_files:
148                     conf_files[conf_file['dest']] = all_conf_files[conf_file_id]
149             
150             # filter out any slices in this nodes slice_id list that may be invalid
151             # (i.e. expired slices)
152             slice_ids = dict.fromkeys(filter(lambda slice_id: slice_id in all_slice_ids, node['slice_ids']))
153             
154             # If not a foreign node, add all of our default system
155             # slices to it.
156             if node['peer_id'] is None:
157                 slice_ids.update(system_slice_ids)
158
159             slivers = [] 
160             
161             for slice in map(lambda id: all_slices[id], slice_ids.keys()):
162                 keys = []
163                 ### still missing in foreign slices
164                 try:
165                     for person in map(lambda id: all_persons[id], slice['person_ids']):
166                         keys += [{'key_type': all_keys[key_id]['key_type'],
167                                   'key': all_keys[key_id]['key']} \
168                                  for key_id in person['key_ids']]
169                 except:
170                     keys += [{'key_type':'missing',
171                               'key':'key caching not implemented yet'}]
172
173                 sliver_attributes = []
174                 attributes = []
175                 ### still missing in foreign slices
176                 try:
177                     slice_attributes = map(lambda id: all_slice_attributes[id],
178                                            slice['slice_attribute_ids'])
179
180                     # Per-node sliver attributes take precedence over
181                     # global slice attributes, so set them first.
182                     for sliver_attribute in filter(lambda a: a['node_id'] == node_id, slice_attributes):
183                         sliver_attributes.append(sliver_attribute['name'])
184                         attributes.append({'name': sliver_attribute['name'],
185                                            'value': sliver_attribute['value']})
186
187                     for slice_attribute in filter(lambda a: a['node_id'] is None, slice_attributes):
188                         # Do not set any global slice attributes for
189                         # which there is at least one sliver attribute
190                         # already set.
191                         if slice_attribute['name'] not in sliver_attributes:
192                             attributes.append({'name': slice_attribute['name'],
193                                                'value': slice_attribute['value']})
194                 except Exception, err:
195                     attributes=[{'name':'attributes caching','value':'not implemented yet'}]
196
197                 slivers.append({
198                     'name': slice['name'],
199                     'slice_id': slice['slice_id'],
200                     'instantiation': slice['instantiation'],
201                     'expires': slice['expires'],
202                     'keys': keys,
203                     'attributes': attributes
204                     })
205             
206             nodes.append({
207                 'timestamp': timestamp,
208                 'node_id': node['node_id'],
209                 'hostname': node['hostname'],
210                 'networks': networks,
211                 'groups': groups,
212                 'conf_files': conf_files.values(),
213                 'slivers': slivers
214                 })
215
216         return nodes