Make PLCAPI calls as user in Aggregate Manager
[sfa.git] / sfa / managers / aggregate_manager_pl.py
1 ### $Id: slices.py 15842 2009-11-22 09:56:13Z anil $
2 ### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/plc/slices.py $
3
4 import datetime
5 import time
6 import traceback
7 import sys
8
9 from types import StringTypes
10 from sfa.util.namespace import *
11 from sfa.util.rspec import *
12 from sfa.util.specdict import *
13 from sfa.util.faults import *
14 from sfa.util.record import SfaRecord
15 from sfa.util.policy import Policy
16 from sfa.util.record import *
17 from sfa.util.sfaticket import SfaTicket
18 from sfa.util.debug import log
19 from sfa.plc.slices import Slices
20 from sfa.trust.credential import Credential
21 import sfa.plc.peers as peers
22 from sfa.plc.network import *
23 from sfa.plc.api import SfaAPI
24 from sfa.plc.slices import *
25
26 """ 
27 Create a new plauth object that the Aggregate Manager can use to execute
28 plshell commands as the authenticated user.
29 """
30 def __get_user_plauth(api, registry, credential, creds, operation, hrn):
31     plauth = None
32
33     user_creds = api.auth.checkCredentials(creds, operation, hrn)
34     user_cred_obj = Credential(string=user_creds[0])
35
36     # If user cred has a parent then the caller is the parent's cred.
37     # This is true for delegated creds.
38     if user_cred_obj.parent:
39         user_hrn = user_cred_obj.parent.get_gid_caller().get_hrn()
40     else:
41         user_hrn = user_cred_obj.get_gid_caller().get_hrn()
42         
43     user_record = registry.Resolve(user_hrn, [credential])[0]
44     email = user_record['email']
45
46     person = api.plshell.GetPersons(api.plauth, email)
47     if person:
48         person_id = person[0]['person_id']
49         # Get the user's session if one exists, create one otherwise
50         session = api.plshell.GetSessions(api.plauth, {'person_id': person_id})
51         if not session:
52             session = api.plshell.AddSession(api.plauth, person_id)
53         else:
54             session = session[0]['session_id']
55
56         # Create new authentication token
57         plauth = {'Username':email, 'AuthMethod':'session', 'session':session}
58
59     return plauth
60
61
62 def __get_registry_objects(slice_xrn, creds, users):
63     """
64
65     """
66     hrn, type = urn_to_hrn(slice_xrn)
67
68     hrn_auth = get_authority(hrn)
69
70     # Build up objects that an SFA registry would return if SFA
71     # could contact the slice's registry directly
72     reg_objects = None
73
74     if users:
75         reg_objects = {}
76
77         site = {}
78         site['site_id'] = 0
79         site['name'] = 'geni.%s' % hrn_auth
80         site['enabled'] = True
81         site['max_slices'] = 100
82
83         # Note:
84         # Is it okay if this login base is the same as one already at this myplc site?
85         # Do we need uniqueness?  Should use hrn_auth instead of just the leaf perhaps?
86         site['login_base'] = get_leaf(hrn_auth)
87         site['abbreviated_name'] = hrn
88         site['max_slivers'] = 1000
89         reg_objects['site'] = site
90
91         slice = {}
92         slice['expires'] = int(time.mktime(Credential(string=creds[0]).get_lifetime().timetuple()))
93         slice['hrn'] = hrn
94         slice['name'] = site['login_base'] + "_" +  get_leaf(hrn)
95         slice['url'] = hrn
96         slice['description'] = hrn
97         slice['pointer'] = 0
98         reg_objects['slice_record'] = slice
99
100         reg_objects['users'] = {}
101         for user in users:
102             user['key_ids'] = []
103             hrn, _ = urn_to_hrn(user['urn'])
104             user['email'] = hrn + "@geni.net"
105             user['first_name'] = hrn
106             user['last_name'] = hrn
107             reg_objects['users'][user['email']] = user
108
109         return reg_objects
110
111 def __get_hostnames(nodes):
112     hostnames = []
113     for node in nodes:
114         hostnames.append(node.hostname)
115     return hostnames
116
117 def get_version():
118     version = {}
119     version['geni_api'] = 1
120     version['sfa'] = 1
121     return version
122
123 def slice_status(api, slice_xrn, creds):
124     result = {}
125     result['geni_urn'] = slice_xrn
126     result['geni_status'] = 'unknown'
127     result['geni_resources'] = {}
128     return result
129
130 def create_slice(api, slice_xrn, creds, rspec, users):
131     """
132     Create the sliver[s] (slice) at this aggregate.    
133     Verify HRN and initialize the slice record in PLC if necessary.
134     """
135     reg_objects = __get_registry_objects(slice_xrn, creds, users)
136
137     hrn, type = urn_to_hrn(slice_xrn)
138     peer = None
139     slices = Slices(api)
140     peer = slices.get_peer(hrn)
141     sfa_peer = slices.get_sfa_peer(hrn)
142     registry = api.registries[api.hrn]
143     credential = api.getCredential()
144     site_id, remote_site_id = slices.verify_site(registry, credential, hrn, 
145                                                  peer, sfa_peer, reg_objects)
146
147     slice_record = slices.verify_slice(registry, credential, hrn, site_id, 
148                                 remote_site_id, peer, sfa_peer, reg_objects)
149
150     user_plauth = __get_user_plauth(api, registry, credential, creds, 
151                                     "createsliver", hrn)
152
153     # The Network instance will use user_plauth to call the PLCAPI
154     network = Network(api, user_plauth)
155
156     slice = network.get_slice(api, hrn)
157     slice.peer_id = slice_record['peer_slice_id']
158     current = __get_hostnames(slice.get_nodes())
159     
160     network.addRSpec(rspec, api.config.SFA_AGGREGATE_RSPEC_SCHEMA)
161     request = __get_hostnames(network.nodesWithSlivers())
162     
163     # remove nodes not in rspec
164     deleted_nodes = list(set(current).difference(request))
165
166     # add nodes from rspec
167     added_nodes = list(set(request).difference(current))
168     
169     if peer:
170         api.plshell.UnBindObjectFromPeer(api.plauth, 'slice', slice.id, peer)
171
172     api.plshell.AddSliceToNodes(user_plauth, slice.name, added_nodes) 
173     api.plshell.DeleteSliceFromNodes(user_plauth, slice.name, deleted_nodes)
174
175     network.updateSliceTags()
176
177     if peer:
178         api.plshell.BindObjectToPeer(api.plauth, 'slice', slice.id, peer, 
179                                      slice.peer_id)
180
181     # print network.toxml()
182     return True
183
184
185 def renew_slice(api, xrn, creds, exipration_time):
186     hrn, type = urn_to_hrn(xrn)
187     slicename = hrn_to_pl_slicename(hrn)
188     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
189     if not slices:
190         raise RecordNotFound(hrn)
191     slice = slices[0]
192     slice['expires'] = expiration_time
193     api.plshell.UpdateSlice(api.plauth, slice['slice_id'], slice)
194     return 1         
195
196 def start_slice(api, xrn, creds):
197     hrn, type = urn_to_hrn(xrn)
198     slicename = hrn_to_pl_slicename(hrn)
199     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
200     if not slices:
201         raise RecordNotFound(hrn)
202     slice_id = slices[0]['slice_id']
203     slice_tags = api.plshell.GetSliceTags(api.plauth, {'slice_id': slice_id, 'tagname': 'enabled'}, ['slice_tag_id'])
204     # just remove the tag if it exists
205     if slice_tags:
206         api.plshell.DeleteSliceTag(api.plauth, slice_tags[0]['slice_tag_id'])
207
208     return 1
209  
210 def stop_slice(api, xrn, creds):
211     hrn, type = urn_to_hrn(xrn)
212     slicename = hrn_to_pl_slicename(hrn)
213     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
214     if not slices:
215         raise RecordNotFound(hrn)
216     slice_id = slices[0]['slice_id']
217     slice_tags = api.plshell.GetSliceTags(api.plauth, {'slice_id': slice_id, 'tagname': 'enabled'})
218     if not slice_tags:
219         api.plshell.AddSliceTag(api.plauth, slice_id, 'enabled', '0')
220     elif slice_tags[0]['value'] != "0":
221         tag_id = attributes[0]['slice_tag_id']
222         api.plshell.UpdateSliceTag(api.plauth, tag_id, '0')
223     return 1
224
225 def reset_slice(api, xrn):
226     # XX not implemented at this interface
227     return 1
228
229 def delete_slice(api, xrn, creds):
230     hrn, type = urn_to_hrn(xrn)
231     slicename = hrn_to_pl_slicename(hrn)
232     slices = api.plshell.GetSlices(api.plauth, {'name': slicename})
233     if not slices:
234         return 1
235     slice = slices[0]
236
237     # determine if this is a peer slice
238     peer = peers.get_peer(api, hrn)
239     if peer:
240         api.plshell.UnBindObjectFromPeer(api.plauth, 'slice', slice['slice_id'], peer)
241     api.plshell.DeleteSliceFromNodes(api.plauth, slicename, slice['node_ids'])
242     if peer:
243         api.plshell.BindObjectToPeer(api.plauth, 'slice', slice['slice_id'], peer, slice['peer_slice_id'])
244     return 1
245
246 def get_slices(api, creds):
247     # look in cache first
248     if api.cache:
249         slices = api.cache.get('slices')
250         if slices:
251             return slices
252
253     # get data from db 
254     slices = api.plshell.GetSlices(api.plauth, {'peer_id': None}, ['name'])
255     slice_hrns = [slicename_to_hrn(api.hrn, slice['name']) for slice in slices]
256     slice_urns = [hrn_to_urn(slice_hrn, 'slice') for slice_hrn in slice_hrns]
257
258     # cache the result
259     if api.cache:
260         api.cache.add('slices', slice_urns) 
261
262     return slice_urns
263     
264 def get_rspec(api, creds, options):
265     # get slice's hrn from options
266     xrn = options.get('geni_slice_urn', None)
267     hrn, type = urn_to_hrn(xrn)
268
269     """
270     # look in cache first
271     if api.cache and not xrn:
272         rspec = api.cache.get('nodes')
273         if rspec:
274             return rspec 
275     """
276
277     registry = api.registries[api.hrn]
278     credential = api.getCredential()
279     user_plauth = __get_user_plauth(api, registry, credential, creds, 
280                                     "listnodes", hrn)
281
282     # The Network instance will use user_plauth to call the PLCAPI
283     network = Network(api, user_plauth)
284     if (hrn):
285         if network.get_slice(api, hrn):
286             network.addSlice()
287
288     rspec = network.toxml()
289
290     """
291     # cache the result
292     if api.cache and not xrn:
293         api.cache.add('nodes', rspec)
294     """
295     return rspec
296
297
298 def get_ticket(api, xrn, creds, rspec, users):
299
300     reg_objects = __get_registry_objects(xrn, creds, users)
301
302     slice_hrn, type = urn_to_hrn(xrn)
303     slices = Slices(api)
304     peer = slices.get_peer(slice_hrn)
305     sfa_peer = slices.get_sfa_peer(slice_hrn)
306
307     # get the slice record
308     registry = api.registries[api.hrn]
309     credential = api.getCredential()
310     records = registry.Resolve(xrn, credential)
311
312     # similar to create_slice, we must verify that the required records exist
313     # at this aggregate before we can issue a ticket
314     site_id, remote_site_id = slices.verify_site(registry, credential, slice_hrn,
315                                                  peer, sfa_peer, reg_objects)
316     slice = slices.verify_slice(registry, credential, slice_hrn, site_id,
317                                 remote_site_id, peer, sfa_peer, reg_objects)
318
319     # make sure we get a local slice record
320     record = None
321     for tmp_record in records:
322         if tmp_record['type'] == 'slice' and \
323            not tmp_record['peer_authority']:
324             record = SliceRecord(dict=tmp_record)
325     if not record:
326         raise RecordNotFound(slice_hrn)
327
328     # get sliver info
329     slivers = Slices(api).get_slivers(slice_hrn)
330     if not slivers:
331         raise SliverDoesNotExist(slice_hrn)
332
333     # get initscripts
334     initscripts = []
335     data = {
336         'timestamp': int(time.time()),
337         'initscripts': initscripts,
338         'slivers': slivers
339     }
340
341     # create the ticket
342     object_gid = record.get_gid_object()
343     new_ticket = SfaTicket(subject = object_gid.get_subject())
344     new_ticket.set_gid_caller(api.auth.client_gid)
345     new_ticket.set_gid_object(object_gid)
346     new_ticket.set_issuer(key=api.key, subject=api.hrn)
347     new_ticket.set_pubkey(object_gid.get_pubkey())
348     new_ticket.set_attributes(data)
349     new_ticket.set_rspec(rspec)
350     #new_ticket.set_parent(api.auth.hierarchy.get_auth_ticket(auth_hrn))
351     new_ticket.encode()
352     new_ticket.sign()
353
354     return new_ticket.save_to_string(save_parents=True)
355
356
357
358 def main():
359     api = SfaAPI()
360     """
361     rspec = get_rspec(api, "plc.princeton.sapan", None)
362     #rspec = get_rspec(api, "plc.princeton.coblitz", None)
363     #rspec = get_rspec(api, "plc.pl.sirius", None)
364     print rspec
365     """
366     f = open(sys.argv[1])
367     xml = f.read()
368     f.close()
369     create_slice(api, "plc.princeton.sapan", xml)
370
371 if __name__ == "__main__":
372     main()