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