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