- pull bulk of functionality into get_slivers() utility function so that
[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 get_slivers(api, slice_filter, node = None):
18     # Get slice information
19     slices = Slices(api, slice_filter)
20
21     # Build up list of users and slice attributes
22     person_ids = set()
23     slice_attribute_ids = set()
24     for slice in slices:
25         person_ids.update(slice['person_ids'])
26         slice_attribute_ids.update(slice['slice_attribute_ids'])
27
28     # Get user information
29     all_persons = Persons(api, person_ids).dict()
30
31     # Build up list of keys
32     key_ids = set()
33     for person in all_persons.values():
34         key_ids.update(person['key_ids'])
35
36     # Get user account keys
37     all_keys = Keys(api, key_ids).dict()
38
39     # Get slice attributes
40     all_slice_attributes = SliceAttributes(api, slice_attribute_ids).dict()
41
42     slivers = []
43     for slice in slices:
44         keys = []
45         for person_id in slice['person_ids']:
46             if person_id in all_persons:
47                 person = all_persons[person_id]
48                 if not person['enabled']:
49                     continue
50                 for key_id in person['key_ids']:
51                     if key_id in all_keys:
52                         key = all_keys[key_id]
53                         keys += [{'key_type': key['key_type'],
54                                   'key': key['key']}]
55
56         attributes = []
57
58         # All (per-node and global) attributes for this slice
59         slice_attributes = []
60         for slice_attribute_id in slice['slice_attribute_ids']:
61             if slice_attribute_id in all_slice_attributes:
62                 slice_attributes.append(all_slice_attributes[slice_attribute_id])
63
64         # Per-node sliver attributes take precedence over global
65         # slice attributes, so set them first.
66         sliver_attributes = []
67
68         if node is not None:
69             for sliver_attribute in filter(lambda a: a['node_id'] == node['node_id'], slice_attributes):
70                 sliver_attributes.append(sliver_attribute['name'])
71                 attributes.append({'name': sliver_attribute['name'],
72                                    'value': sliver_attribute['value']})
73
74         for slice_attribute in filter(lambda a: a['node_id'] is None, slice_attributes):
75             # Do not set any global slice attributes for
76             # which there is at least one sliver attribute
77             # already set.
78             if slice_attribute['name'] not in sliver_attributes:
79                 attributes.append({'name': slice_attribute['name'],
80                                    'value': slice_attribute['value']})
81
82         slivers.append({
83             'name': slice['name'],
84             'slice_id': slice['slice_id'],
85             'instantiation': slice['instantiation'],
86             'expires': slice['expires'],
87             'keys': keys,
88             'attributes': attributes
89             })
90
91     return slivers
92
93 class GetSlivers(Method):
94     """
95     Returns a struct containing information about the specified node
96     (or calling node, if called by a node and node_id_or_hostname is
97     not specified), including the current set of slivers bound to the
98     node.
99
100     All of the information returned by this call can be gathered from
101     other calls, e.g. GetNodes, GetNodeNetworks, GetSlices, etc. This
102     function exists almost solely for the benefit of Node Manager.
103     """
104
105     roles = ['admin', 'node']
106
107     accepts = [
108         Auth(),
109         Mixed(Node.fields['node_id'],
110               Node.fields['hostname']),
111         ]
112
113     returns = {
114         'timestamp': Parameter(int, "Timestamp of this call, in seconds since UNIX epoch"),
115         'node_id': Node.fields['node_id'],
116         'hostname': Node.fields['hostname'],
117         'networks': [NodeNetwork.fields],
118         'groups': [NodeGroup.fields['name']],
119         'conf_files': [ConfFile.fields],
120         'slivers': [{
121             'name': Slice.fields['name'],
122             'slice_id': Slice.fields['slice_id'],
123             'instantiation': Slice.fields['instantiation'],
124             'expires': Slice.fields['expires'],
125             'keys': [{
126                 'key_type': Key.fields['key_type'],
127                 'key': Key.fields['key']
128             }],
129             'attributes': [{
130                 'name': SliceAttribute.fields['name'],
131                 'value': SliceAttribute.fields['value']
132             }]
133         }]
134     }
135
136     def call(self, auth, node_id_or_hostname = None):
137         timestamp = int(time.time())
138
139         # Get node
140         if node_id_or_hostname is None:
141             if isinstance(self.caller, Node):
142                 node = self.caller
143             else:
144                 raise PLCInvalidArgument, "'node_id_or_hostname' not specified"
145         else:
146             nodes = Nodes(self.api, [node_id_or_hostname])
147             if not nodes:
148                 raise PLCInvalidArgument, "No such node"
149             node = nodes[0]
150
151             if node['peer_id'] is not None:
152                 raise PLCInvalidArgument, "Not a local node"
153
154         # Get nodenetwork information
155         networks = NodeNetworks(self.api, node['nodenetwork_ids'])
156
157         # Get node group information
158         nodegroups = NodeGroups(self.api, node['nodegroup_ids']).dict('name')
159         groups = nodegroups.keys()
160
161         # Get all (enabled) configuration files
162         all_conf_files = ConfFiles(self.api, {'enabled': True}).dict()
163         conf_files = {}
164
165         # Global configuration files are the default. If multiple
166         # entries for the same global configuration file exist, it is
167         # undefined which one takes precedence.
168         for conf_file in all_conf_files.values():
169             if not conf_file['node_ids'] and not conf_file['nodegroup_ids']:
170                 conf_files[conf_file['dest']] = conf_file
171         
172         # Node group configuration files take precedence over global
173         # ones. If a node belongs to multiple node groups for which
174         # the same configuration file is defined, it is undefined
175         # which one takes precedence.
176         for nodegroup in nodegroups.values():
177             for conf_file_id in nodegroup['conf_file_ids']:
178                 if conf_file_id in all_conf_files:
179                     conf_files[conf_file['dest']] = all_conf_files[conf_file_id]
180         
181         # Node configuration files take precedence over node group
182         # configuration files.
183         for conf_file_id in node['conf_file_ids']:
184             if conf_file_id in all_conf_files:
185                 conf_files[conf_file['dest']] = all_conf_files[conf_file_id]
186             
187         # Get system slices
188         system_slice_attributes = SliceAttributes(self.api, {'name': 'system', 'value': '1'}).dict('slice_id')
189         system_slice_ids = system_slice_attributes.keys()
190
191         slivers = get_slivers(self.api, system_slice_ids + node['slice_ids'], node)
192
193         return {
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             }