- re-enable return_fields specification
[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.Persons import Person, Persons
14 from PLC.Keys import Key, Keys
15 from PLC.SliceAttributes import SliceAttribute, SliceAttributes
16
17 def hashref(rows, key_field):
18     d = {}
19     for row in rows:
20         d[row[key_field]] = row
21     return d
22
23 class GetSlivers(Method):
24     """
25     Returns an array of structs representing nodes and their slivers
26     (slices bound to nodes). If node_filter is specified, only
27     information about the specified nodes will be returned. If
28     node_filter is not specified and called by a node, only
29     information about the caller will be returned.
30
31     All of the information returned by this call can be gathered from
32     other calls, e.g. GetNodes, GetNodeNetworks, GetSlices, etc. This
33     function exists primarily for the benefit of Node Manager and
34     Federation Manager.
35     """
36
37     roles = ['admin', 'node']
38
39     accepts = [
40         Auth(),
41         Mixed([Mixed(Node.fields['node_id'],
42                      Node.fields['hostname'])],
43               Filter(Node.fields)),
44         ]
45
46     returns = [{
47         'timestamp': Parameter(int, "Timestamp of this call, in seconds since UNIX epoch"),
48         'node_id': Node.fields['node_id'],
49         'hostname': Node.fields['hostname'],
50         'networks': [NodeNetwork.fields],
51         'groups': [NodeGroup.fields['name']],
52         'conf_files': [ConfFile.fields],
53         'slivers': [{
54             'name': Slice.fields['name'],
55             'slice_id': Slice.fields['slice_id'],
56             'instantiation': Slice.fields['instantiation'],
57             'expires': Slice.fields['expires'],
58             'keys': [{
59                 'key_type': Key.fields['key_type'],
60                 'key': Key.fields['key']
61             }],
62             'attributes': [{
63                 'name': SliceAttribute.fields['name'],
64                 'value': SliceAttribute.fields['value']
65             }]
66         }]
67     }]
68
69     def call(self, auth, node_filter = None):
70         timestamp = int(time.time())
71
72         if node_filter is None and isinstance(self.caller, Node):
73             all_nodes = {self.caller['node_id']: self.caller}
74         else:
75             all_nodes = hashref(Nodes(self.api, node_filter), 'node_id')
76             # XXX Add foreign nodes
77
78         nodenetwork_ids = set()
79         nodegroup_ids = set()
80         slice_ids = set()
81         for node_id, node in all_nodes.iteritems():
82             nodenetwork_ids.update(node['nodenetwork_ids'])
83             nodegroup_ids.update(node['nodegroup_ids'])
84             slice_ids.update(node['slice_ids'])
85
86         # Get nodenetwork information
87         all_nodenetworks = hashref(NodeNetworks(self.api, nodenetwork_ids), 'nodenetwork_id')
88
89         # Get node group information
90         all_nodegroups = hashref(NodeGroups(self.api, nodegroup_ids), 'nodegroup_id')
91
92         # Get (enabled) configuration files
93         all_conf_files = hashref(ConfFiles(self.api, {'enabled': True}), 'conf_file_id')
94
95         if slice_ids:
96             # Get slices
97             all_slices = hashref(Slices(self.api, slice_ids), 'slice_id')
98
99             person_ids = set()
100             slice_attribute_ids = set()
101             for slice_id, slice in all_slices.iteritems():
102                 person_ids.update(slice['person_ids'])
103                 slice_attribute_ids.update(slice['slice_attribute_ids'])
104
105             # Get user accounts
106             all_persons = hashref(Persons(self.api, person_ids), 'person_id')
107
108             key_ids = set()
109             for person_id, person in all_persons.iteritems():
110                 key_ids.update(person['key_ids'])
111
112             # Get user account keys
113             all_keys = hashref(Keys(self.api, key_ids), 'key_id')
114
115             # Get slice attributes
116             all_slice_attributes = hashref(SliceAttributes(self.api, slice_attribute_ids), 'slice_attribute_id')
117
118         nodes = []
119         for node_id, node in all_nodes.iteritems():
120             networks = [all_nodenetworks[nodenetwork_id] for nodenetwork_id in node['nodenetwork_ids']]
121             nodegroups = [all_nodegroups[nodegroup_id] for nodegroup_id in node['nodegroup_ids']]
122             groups = [nodegroup['name'] for nodegroup in nodegroups]
123
124             # If multiple entries for the same global configuration
125             # file exist, it is undefined which one takes precedence.
126             conf_files = {}
127             for conf_file in all_conf_files.values():
128                 if not conf_file['node_ids'] and not conf_file['nodegroup_ids']:
129                     conf_files[conf_file['dest']] = conf_file
130
131             # If a node belongs to multiple node
132             # groups for which the same configuration file is defined,
133             # it is undefined which one takes precedence.
134             for nodegroup in nodegroups:
135                 for conf_file_id in nodegroup['conf_file_ids']:
136                     if conf_file_id in all_conf_files:
137                         conf_files[conf_file['dest']] = all_conf_files[conf_file_id]
138
139             # Node configuration files always take precedence over
140             # node group configuration files.
141             for conf_file_id in node['conf_file_ids']:
142                 if conf_file_id in all_conf_files:
143                     conf_files[conf_file['dest']] = all_conf_files[conf_file_id]
144
145             slivers = []
146             for slice in map(lambda id: all_slices[id], node['slice_ids']):
147                 keys = []
148                 for person in map(lambda id: all_persons[id], slice['person_ids']):
149                     keys += [{'key_type': all_keys[key_id]['key_type'],
150                               'key': all_keys[key_id]['key']} \
151                              for key_id in person['key_ids']]
152
153                 attributes = {}
154                 for slice_attribute in map(lambda id: all_slice_attributes[id],
155                                            slice['slice_attribute_ids']):
156                     # Per-node sliver attributes (slice attributes
157                     # with non-null node_id fields) take precedence
158                     # over global slice attributes.
159                     if not attributes.has_key(slice_attribute['name']) or \
160                        slice_attribute['node_id'] is not None:
161                         attributes[slice_attribute['name']] = {
162                             'name': slice_attribute['name'],
163                             'value': slice_attribute['value']
164                             }
165
166                 slivers.append({
167                     'name': slice['name'],
168                     'slice_id': slice['slice_id'],
169                     'instantiation': slice['instantiation'],
170                     'expires': slice['expires'],
171                     'keys': keys,
172                     'attributes': attributes.values()
173                     })
174
175             nodes.append({
176                 'timestamp': timestamp,
177                 'node_id': node['node_id'],
178                 'hostname': node['hostname'],
179                 'networks': networks,
180                 'groups': groups,
181                 'conf_files': conf_files.values(),
182                 'slivers': slivers
183                 })
184
185         return nodes