63e30c08a84cb7db70ad3314e44c8e34535771b3
[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.Sites import Sites
16 from PLC.Roles import Roles
17 from PLC.Keys import Key, Keys
18 from PLC.SliceTags import SliceTag, SliceTags
19 from PLC.InitScripts import InitScript, InitScripts
20
21 # XXX used to check if slice expiration time is sane
22 MAXINT =  2L**31-1
23
24 def get_slivers(api, slice_filter, node = None):
25     # Get slice information
26     slices = Slices(api, slice_filter, ['slice_id', 'name', 'instantiation', 'expires', 'person_ids', 'slice_tag_ids'])
27
28     # Build up list of users and slice attributes
29     person_ids = set()
30     slice_tag_ids = set()
31     for slice in slices:
32         person_ids.update(slice['person_ids'])
33         slice_tag_ids.update(slice['slice_tag_ids'])
34
35     # Get user information
36     all_persons = Persons(api, {'person_id':person_ids,'enabled':True}, ['person_id', 'enabled', 'key_ids']).dict()
37
38     # Build up list of keys
39     key_ids = set()
40     for person in all_persons.values():
41         key_ids.update(person['key_ids'])
42
43     # Get user account keys
44     all_keys = Keys(api, key_ids, ['key_id', 'key', 'key_type']).dict()
45
46     # Get slice attributes
47     all_slice_tags = SliceTags(api, slice_tag_ids).dict()
48
49     slivers = []
50     for slice in slices:
51         keys = []
52         for person_id in slice['person_ids']:
53             if person_id in all_persons:
54                 person = all_persons[person_id]
55                 if not person['enabled']:
56                     continue
57                 for key_id in person['key_ids']:
58                     if key_id in all_keys:
59                         key = all_keys[key_id]
60                         keys += [{'key_type': key['key_type'],
61                                   'key': key['key']}]
62
63         attributes = []
64
65         # All (per-node and global) attributes for this slice
66         slice_tags = []
67         for slice_tag_id in slice['slice_tag_ids']:
68             if slice_tag_id in all_slice_tags:
69                 slice_tags.append(all_slice_tags[slice_tag_id])
70
71         # Per-node sliver attributes take precedence over global
72         # slice attributes, so set them first.
73         # Then comes nodegroup slice attributes
74         # Followed by global slice attributes
75         sliver_attributes = []
76
77         if node is not None:
78             for sliver_attribute in filter(lambda a: a['node_id'] == node['node_id'], slice_tags):
79                 sliver_attributes.append(sliver_attribute['tagname'])
80                 attributes.append({'tagname': sliver_attribute['tagname'],
81                                    'value': sliver_attribute['value']})
82
83             # set nodegroup slice attributes
84             for slice_tag in filter(lambda a: a['nodegroup_id'] in node['nodegroup_ids'], slice_tags):
85                 # Do not set any nodegroup slice attributes for
86                 # which there is at least one sliver attribute
87                 # already set.
88                 if slice_tag not in slice_tags:
89                     attributes.append({'tagname': slice_tag['tagname'],
90                                    'value': slice_tag['value']})
91
92         for slice_tag in filter(lambda a: a['node_id'] is None, slice_tags):
93             # Do not set any global slice attributes for
94             # which there is at least one sliver attribute
95             # already set.
96             if slice_tag['tagname'] not in sliver_attributes:
97                 attributes.append({'tagname': slice_tag['tagname'],
98                                    'value': slice_tag['value']})
99
100         # XXX Sanity check; though technically this should be a system invariant
101         # checked with an assertion
102         if slice['expires'] > MAXINT:  slice['expires']= MAXINT
103
104         slivers.append({
105             'name': slice['name'],
106             'slice_id': slice['slice_id'],
107             'instantiation': slice['instantiation'],
108             'expires': slice['expires'],
109             'keys': keys,
110             'attributes': attributes
111             })
112
113     return slivers
114
115 class v43GetSlivers(Method):
116     """
117     Returns a struct containing information about the specified node
118     (or calling node, if called by a node and node_id_or_hostname is
119     not specified), including the current set of slivers bound to the
120     node.
121
122     All of the information returned by this call can be gathered from
123     other calls, e.g. GetNodes, GetInterfaces, GetSlices, etc. This
124     function exists almost solely for the benefit of Node Manager.
125     """
126
127     roles = ['admin', 'node']
128
129     accepts = [
130         Auth(),
131         Mixed(Node.fields['node_id'],
132               Node.fields['hostname']),
133         ]
134
135     returns = {
136         'timestamp': Parameter(int, "Timestamp of this call, in seconds since UNIX epoch"),
137         'node_id': Node.fields['node_id'],
138         'hostname': Node.fields['hostname'],
139         'networks': [Interface.fields],
140         'groups': [NodeGroup.fields['groupname']],
141         'conf_files': [ConfFile.fields],
142         'initscripts': [InitScript.fields],
143         'accounts': [{
144             'name': Parameter(str, "unix style account name", max = 254),
145             'keys': [{
146                 'key_type': Key.fields['key_type'],
147                 'key': Key.fields['key']
148             }],
149             }],
150         'slivers': [{
151             'name': Slice.fields['name'],
152             'slice_id': Slice.fields['slice_id'],
153             'instantiation': Slice.fields['instantiation'],
154             'expires': Slice.fields['expires'],
155             'keys': [{
156                 'key_type': Key.fields['key_type'],
157                 'key': Key.fields['key']
158             }],
159             'attributes': [{
160                 'tagname': SliceTag.fields['tagname'],
161                 'value': SliceTag.fields['value']
162             }]
163         }]
164     }
165
166     def call(self, auth, node_id_or_hostname = None):
167         timestamp = int(time.time())
168
169         # Get node
170         if node_id_or_hostname is None:
171             if isinstance(self.caller, Node):
172                 node = self.caller
173             else:
174                 raise PLCInvalidArgument, "'node_id_or_hostname' not specified"
175         else:
176             nodes = Nodes(self.api, [node_id_or_hostname])
177             if not nodes:
178                 raise PLCInvalidArgument, "No such node"
179             node = nodes[0]
180
181             if node['peer_id'] is not None:
182                 raise PLCInvalidArgument, "Not a local node"
183
184         # Get interface information
185         networks = Interfaces(self.api, node['interface_ids'])
186
187         # Get node group information
188         nodegroups = NodeGroups(self.api, node['nodegroup_ids']).dict('groupname')
189         groups = nodegroups.keys()
190
191         # Get all (enabled) configuration files
192         all_conf_files = ConfFiles(self.api, {'enabled': True}).dict()
193         conf_files = {}
194
195         # Global configuration files are the default. If multiple
196         # entries for the same global configuration file exist, it is
197         # undefined which one takes precedence.
198         for conf_file in all_conf_files.values():
199             if not conf_file['node_ids'] and not conf_file['nodegroup_ids']:
200                 conf_files[conf_file['dest']] = conf_file
201         
202         # Node group configuration files take precedence over global
203         # ones. If a node belongs to multiple node groups for which
204         # the same configuration file is defined, it is undefined
205         # which one takes precedence.
206         for nodegroup in nodegroups.values():
207             for conf_file_id in nodegroup['conf_file_ids']:
208                 if conf_file_id in all_conf_files:
209                     conf_file = all_conf_files[conf_file_id]
210                     conf_files[conf_file['dest']] = conf_file
211         
212         # Node configuration files take precedence over node group
213         # configuration files.
214         for conf_file_id in node['conf_file_ids']:
215             if conf_file_id in all_conf_files:
216                 conf_file = all_conf_files[conf_file_id]
217                 conf_files[conf_file['dest']] = conf_file            
218
219         # Get all (enabled) initscripts
220         initscripts = InitScripts(self.api, {'enabled': True})  
221
222         # Get system slices
223         system_slice_tags = SliceTags(self.api, {'tagname': 'system', 'value': '1'}).dict('slice_id')
224         system_slice_ids = system_slice_tags.keys()
225         
226         # Get nm-controller slices
227         controller_and_delegated_slices = Slices(self.api, {'instantiation': ['nm-controller', 'delegated']}, ['slice_id']).dict('slice_id')
228         controller_and_delegated_slice_ids = controller_and_delegated_slices.keys()
229         slice_ids = system_slice_ids + controller_and_delegated_slice_ids + node['slice_ids']
230
231         slivers = get_slivers(self.api, slice_ids, node)
232
233         # get the special accounts and keys needed for the node
234         # root
235         # site_admin
236         accounts = []
237         if False and 'site_id' not in node:
238             nodes = Nodes(self.api, node['node_id'])
239             node = nodes[0]
240
241         def getpersonsitekeys(site_id_or_name,theroles):
242             site_filter = site_id_or_name
243             site_return_filter = ['person_ids']
244             sites = Sites(self.api, site_filter, site_return_filter)
245             site = sites[0]
246             person_filter =  {'person_id':site['person_ids'],'enabled':True}
247             person_return_filter = ['person_id', 'enabled', 'key_ids','role_ids','roles'] 
248             site_persons = Persons(self.api, person_filter, person_return_filter)
249
250             # collect the keys into a table to weed out duplicates
251             site_keys = {}
252             for site_person in site_persons:
253                 if site_person['enabled'] is False: continue
254                 more = True
255                 for role in theroles:
256                     if role in site_person['role_ids']:
257                         keys_filter = site_person['key_ids']
258                         keys_return_filter = ['key_id', 'key', 'key_type']
259                         keys = Keys(self.api, keys_filter, keys_return_filter)
260                         for key in keys:
261                             if key['key_type'] == 'ssh':
262                                 site_keys[key['key']]=None
263             return site_keys.keys()
264
265         # 'site_admin' account setup
266         personsitekeys=getpersonsitekeys(node['site_id'],['pi','tech'])
267         accounts.append({'name':'site_admin','keys':personsitekeys})
268
269         # 'root' account setup on nodes from all 'admin' users
270         # registered with the PLC main site
271         personsitekeys=getpersonsitekeys(self.api.config.PLC_SLICE_PREFIX,['admin'])
272         accounts.append({'name':'root','keys':personsitekeys})
273
274         node.update_last_contact()
275
276         return {
277             'timestamp': timestamp,
278             'node_id': node['node_id'],
279             'hostname': node['hostname'],
280             'networks': networks,
281             'groups': groups,
282             'conf_files': conf_files.values(),
283             'initscripts': initscripts,
284             'slivers': slivers,
285             'accounts': accounts
286             }
287
288 class v42GetSlivers(v43GetSlivers):
289     """
290     Legacy wrapper for v43GetSlivers.
291     """
292
293     def call(self, auth, node_id_or_hostname = None):
294         result = v43GetSlivers.call(self,auth,node_id_or_hostname)
295         networks = result['networks']
296
297         for i in range(0,len(networks)):
298             network = networks[i]
299             if network.has_key("interface_id"):
300                 network['nodenetwork_id']=network['interface_id']
301             if network.has_key("interface_tag_ids"):
302                 network['nodenetwork_setting_ids']=network['interface_tag_ids']
303             networks[i]=network
304
305         result['networks']=networks
306         return result
307
308 class GetSlivers(v42GetSlivers):
309     """
310     Returns a struct containing information about the specified node
311     (or calling node, if called by a node and node_id_or_hostname is
312     not specified), including the current set of slivers bound to the
313     node.
314
315     All of the information returned by this call can be gathered from
316     other calls, e.g. GetNodes, GetInterfaces, GetSlices, etc. This
317     function exists almost solely for the benefit of Node Manager.
318     """
319
320     pass