renaming SliceAttribute into SliceTag and InterfaceSetting into InterfaceTag
[plcapi.git] / PLC / Methods / GetSlivers.py
1 # $Id#
2 import time
3
4 from PLC.Faults import *
5 from PLC.Method import Method
6 from PLC.Parameter import Parameter, Mixed
7 from PLC.Filter import Filter
8 from PLC.Auth import Auth
9 from PLC.Nodes import Node, Nodes
10 from PLC.Interfaces import Interface, Interfaces
11 from PLC.NodeGroups import NodeGroup, NodeGroups
12 from PLC.ConfFiles import ConfFile, ConfFiles
13 from PLC.Slices import Slice, Slices
14 from PLC.Persons import Person, Persons
15 from PLC.Keys import Key, Keys
16 from PLC.SliceTags import SliceTag, SliceTags
17 from PLC.InitScripts import InitScript, InitScripts
18
19 def get_slivers(api, slice_filter, node = None):
20     # Get slice information
21     slices = Slices(api, slice_filter, ['slice_id', 'name', 'instantiation', 'expires', 'person_ids', 'slice_tag_ids'])
22
23     # Build up list of users and slice attributes
24     person_ids = set()
25     slice_tag_ids = set()
26     for slice in slices:
27         person_ids.update(slice['person_ids'])
28         slice_tag_ids.update(slice['slice_tag_ids'])
29
30     # Get user information
31     all_persons = Persons(api, {'person_id':person_ids,'enabled':True}, ['person_id', 'enabled', 'key_ids']).dict()
32
33     # Build up list of keys
34     key_ids = set()
35     for person in all_persons.values():
36         key_ids.update(person['key_ids'])
37
38     # Get user account keys
39     all_keys = Keys(api, key_ids, ['key_id', 'key', 'key_type']).dict()
40
41     # Get slice attributes
42     all_slice_tags = SliceTags(api, slice_tag_ids).dict()
43
44     slivers = []
45     for slice in slices:
46         keys = []
47         for person_id in slice['person_ids']:
48             if person_id in all_persons:
49                 person = all_persons[person_id]
50                 if not person['enabled']:
51                     continue
52                 for key_id in person['key_ids']:
53                     if key_id in all_keys:
54                         key = all_keys[key_id]
55                         keys += [{'key_type': key['key_type'],
56                                   'key': key['key']}]
57
58         attributes = []
59
60         # All (per-node and global) attributes for this slice
61         slice_tags = []
62         for slice_tag_id in slice['slice_tag_ids']:
63             if slice_tag_id in all_slice_tags:
64                 slice_tags.append(all_slice_tags[slice_tag_id])
65
66         # Per-node sliver attributes take precedence over global
67         # slice attributes, so set them first.
68         # Then comes nodegroup slice attributes
69         # Followed by global slice attributes
70         sliver_attributes = []
71
72         if node is not None:
73             for sliver_attribute in filter(lambda a: a['node_id'] == node['node_id'], slice_tags):
74                 sliver_attributes.append(sliver_attribute['tagname'])
75                 attributes.append({'tagname': sliver_attribute['tagname'],
76                                    'value': sliver_attribute['value']})
77
78             # set nodegroup slice attributes
79             for slice_tag in filter(lambda a: a['nodegroup_id'] in node['nodegroup_ids'], slice_tags):
80                 # Do not set any nodegroup slice attributes for
81                 # which there is at least one sliver attribute
82                 # already set.
83                 if slice_tag['tagname'] not in slice_tags:
84                     attributes.append({'tagname': slice_tag['tagname'],
85                                    'value': slice_tag['value']})
86
87         for slice_tag in filter(lambda a: a['node_id'] is None, slice_tags):
88             # Do not set any global slice attributes for
89             # which there is at least one sliver attribute
90             # already set.
91             if slice_tag['tagname'] not in sliver_attributes:
92                 attributes.append({'tagname': slice_tag['tagname'],
93                                    'value': slice_tag['value']})
94
95         slivers.append({
96             'name': slice['name'],
97             'slice_id': slice['slice_id'],
98             'instantiation': slice['instantiation'],
99             'expires': slice['expires'],
100             'keys': keys,
101             'attributes': attributes
102             })
103
104     return slivers
105
106 class GetSlivers(Method):
107     """
108     Returns a struct containing information about the specified node
109     (or calling node, if called by a node and node_id_or_hostname is
110     not specified), including the current set of slivers bound to the
111     node.
112
113     All of the information returned by this call can be gathered from
114     other calls, e.g. GetNodes, GetInterfaces, GetSlices, etc. This
115     function exists almost solely for the benefit of Node Manager.
116     """
117
118     roles = ['admin', 'node']
119
120     accepts = [
121         Auth(),
122         Mixed(Node.fields['node_id'],
123               Node.fields['hostname']),
124         ]
125
126     returns = {
127         'timestamp': Parameter(int, "Timestamp of this call, in seconds since UNIX epoch"),
128         'node_id': Node.fields['node_id'],
129         'hostname': Node.fields['hostname'],
130         'networks': [Interface.fields],
131         'groups': [NodeGroup.fields['groupname']],
132         'conf_files': [ConfFile.fields],
133         'initscripts': [InitScript.fields],
134         'slivers': [{
135             'name': Slice.fields['name'],
136             'slice_id': Slice.fields['slice_id'],
137             'instantiation': Slice.fields['instantiation'],
138             'expires': Slice.fields['expires'],
139             'keys': [{
140                 'key_type': Key.fields['key_type'],
141                 'key': Key.fields['key']
142             }],
143             'attributes': [{
144                 'tagname': SliceTag.fields['tagname'],
145                 'value': SliceTag.fields['value']
146             }]
147         }]
148     }
149
150     def call(self, auth, node_id_or_hostname = None):
151         timestamp = int(time.time())
152
153         # Get node
154         if node_id_or_hostname is None:
155             if isinstance(self.caller, Node):
156                 node = self.caller
157             else:
158                 raise PLCInvalidArgument, "'node_id_or_hostname' not specified"
159         else:
160             nodes = Nodes(self.api, [node_id_or_hostname])
161             if not nodes:
162                 raise PLCInvalidArgument, "No such node"
163             node = nodes[0]
164
165             if node['peer_id'] is not None:
166                 raise PLCInvalidArgument, "Not a local node"
167
168         # Get interface information
169         networks = Interfaces(self.api, node['interface_ids'])
170
171         # Get node group information
172         nodegroups = NodeGroups(self.api, node['nodegroup_ids']).dict('groupname')
173         groups = nodegroups.keys()
174
175         # Get all (enabled) configuration files
176         all_conf_files = ConfFiles(self.api, {'enabled': True}).dict()
177         conf_files = {}
178
179         # Global configuration files are the default. If multiple
180         # entries for the same global configuration file exist, it is
181         # undefined which one takes precedence.
182         for conf_file in all_conf_files.values():
183             if not conf_file['node_ids'] and not conf_file['nodegroup_ids']:
184                 conf_files[conf_file['dest']] = conf_file
185         
186         # Node group configuration files take precedence over global
187         # ones. If a node belongs to multiple node groups for which
188         # the same configuration file is defined, it is undefined
189         # which one takes precedence.
190         for nodegroup in nodegroups.values():
191             for conf_file_id in nodegroup['conf_file_ids']:
192                 if conf_file_id in all_conf_files:
193                     conf_file = all_conf_files[conf_file_id]
194                     conf_files[conf_file['dest']] = conf_file
195         
196         # Node configuration files take precedence over node group
197         # configuration files.
198         for conf_file_id in node['conf_file_ids']:
199             if conf_file_id in all_conf_files:
200                 conf_file = all_conf_files[conf_file_id]
201                 conf_files[conf_file['dest']] = conf_file            
202
203         # Get all (enabled) initscripts
204         initscripts = InitScripts(self.api, {'enabled': True})  
205
206         # Get system slices
207         system_slice_tags = SliceTags(self.api, {'tagname': 'system', 'value': '1'}).dict('slice_id')
208         system_slice_ids = system_slice_tags.keys()
209         
210         # Get nm-controller slices
211         controller_and_delegated_slices = Slices(self.api, {'instantiation': ['nm-controller', 'delegated']}, ['slice_id']).dict('slice_id')
212         controller_and_delegated_slice_ids = controller_and_delegated_slices.keys()
213         slice_ids = system_slice_ids + controller_and_delegated_slice_ids + node['slice_ids']
214
215         slivers = get_slivers(self.api, slice_ids, node)
216
217         node.update_last_contact()
218
219         return {
220             'timestamp': timestamp,
221             'node_id': node['node_id'],
222             'hostname': node['hostname'],
223             'networks': networks,
224             'groups': groups,
225             'conf_files': conf_files.values(),
226             'initscripts': initscripts,
227             'slivers': slivers
228             }