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