merged in master the change from 0.9-17 to 0.9-18 :
[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[:20]
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'] = hrn_auth[:20]
87         site['abbreviated_name'] = hrn_auth[:20]
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     try:
170         if peer:
171             api.plshell.UnBindObjectFromPeer(api.plauth, 'slice', slice.id, peer)
172
173         api.plshell.AddSliceToNodes(user_plauth, slice.name, added_nodes) 
174         api.plshell.DeleteSliceFromNodes(user_plauth, slice.name, deleted_nodes)
175
176         network.updateSliceTags()
177
178     finally:
179         if peer:
180             api.plshell.BindObjectToPeer(api.plauth, 'slice', slice.id, peer, 
181                                          slice.peer_id)
182
183     # print network.toxml()
184     return True
185
186
187 def renew_slice(api, xrn, creds, exipration_time):
188     hrn, type = urn_to_hrn(xrn)
189     slicename = hrn_to_pl_slicename(hrn)
190     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
191     if not slices:
192         raise RecordNotFound(hrn)
193     slice = slices[0]
194     slice['expires'] = expiration_time
195     api.plshell.UpdateSlice(api.plauth, slice['slice_id'], slice)
196     return 1         
197
198 def start_slice(api, xrn, creds):
199     hrn, type = urn_to_hrn(xrn)
200     slicename = hrn_to_pl_slicename(hrn)
201     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
202     if not slices:
203         raise RecordNotFound(hrn)
204     slice_id = slices[0]['slice_id']
205     slice_tags = api.plshell.GetSliceTags(api.plauth, {'slice_id': slice_id, 'tagname': 'enabled'}, ['slice_tag_id'])
206     # just remove the tag if it exists
207     if slice_tags:
208         api.plshell.DeleteSliceTag(api.plauth, slice_tags[0]['slice_tag_id'])
209
210     return 1
211  
212 def stop_slice(api, xrn, creds):
213     hrn, type = urn_to_hrn(xrn)
214     slicename = hrn_to_pl_slicename(hrn)
215     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
216     if not slices:
217         raise RecordNotFound(hrn)
218     slice_id = slices[0]['slice_id']
219     slice_tags = api.plshell.GetSliceTags(api.plauth, {'slice_id': slice_id, 'tagname': 'enabled'})
220     if not slice_tags:
221         api.plshell.AddSliceTag(api.plauth, slice_id, 'enabled', '0')
222     elif slice_tags[0]['value'] != "0":
223         tag_id = attributes[0]['slice_tag_id']
224         api.plshell.UpdateSliceTag(api.plauth, tag_id, '0')
225     return 1
226
227 def reset_slice(api, xrn):
228     # XX not implemented at this interface
229     return 1
230
231 def delete_slice(api, xrn, creds):
232     hrn, type = urn_to_hrn(xrn)
233     slicename = hrn_to_pl_slicename(hrn)
234     slices = api.plshell.GetSlices(api.plauth, {'name': slicename})
235     if not slices:
236         return 1
237     slice = slices[0]
238
239     # determine if this is a peer slice
240     peer = peers.get_peer(api, hrn)
241     try:
242         if peer:
243             api.plshell.UnBindObjectFromPeer(api.plauth, 'slice', slice['slice_id'], peer)
244         api.plshell.DeleteSliceFromNodes(api.plauth, slicename, slice['node_ids'])
245     finally:
246         if peer:
247             api.plshell.BindObjectToPeer(api.plauth, 'slice', slice['slice_id'], peer, slice['peer_slice_id'])
248     return 1
249
250 def get_slices(api, creds):
251     # look in cache first
252     if api.cache:
253         slices = api.cache.get('slices')
254         if slices:
255             return slices
256
257     # get data from db 
258     slices = api.plshell.GetSlices(api.plauth, {'peer_id': None}, ['name'])
259     slice_hrns = [slicename_to_hrn(api.hrn, slice['name']) for slice in slices]
260     slice_urns = [hrn_to_urn(slice_hrn, 'slice') for slice_hrn in slice_hrns]
261
262     # cache the result
263     if api.cache:
264         api.cache.add('slices', slice_urns) 
265
266     return slice_urns
267     
268 def get_rspec(api, creds, options):
269     # get slice's hrn from options
270     xrn = options.get('geni_slice_urn', None)
271     hrn, type = urn_to_hrn(xrn)
272
273     """
274     # look in cache first
275     if api.cache and not xrn:
276         rspec = api.cache.get('nodes')
277         if rspec:
278             return rspec 
279     """
280
281     registry = api.registries[api.hrn]
282     credential = api.getCredential()
283     user_plauth = __get_user_plauth(api, registry, credential, creds, 
284                                     "listnodes", hrn)
285
286     # The Network instance will use user_plauth to call the PLCAPI
287     network = Network(api, user_plauth)
288     if (hrn):
289         if network.get_slice(api, hrn):
290             network.addSlice()
291
292     rspec = network.toxml()
293
294     """
295     # cache the result
296     if api.cache and not xrn:
297         api.cache.add('nodes', rspec)
298     """
299     return rspec
300
301
302 def get_ticket(api, xrn, creds, rspec, users):
303
304     reg_objects = __get_registry_objects(xrn, creds, users)
305
306     slice_hrn, type = urn_to_hrn(xrn)
307     slices = Slices(api)
308     peer = slices.get_peer(slice_hrn)
309     sfa_peer = slices.get_sfa_peer(slice_hrn)
310
311     # get the slice record
312     registry = api.registries[api.hrn]
313     credential = api.getCredential()
314     records = registry.Resolve(xrn, credential)
315
316     # similar to create_slice, we must verify that the required records exist
317     # at this aggregate before we can issue a ticket
318     site_id, remote_site_id = slices.verify_site(registry, credential, slice_hrn,
319                                                  peer, sfa_peer, reg_objects)
320     slice = slices.verify_slice(registry, credential, slice_hrn, site_id,
321                                 remote_site_id, peer, sfa_peer, reg_objects)
322
323     # make sure we get a local slice record
324     record = None
325     for tmp_record in records:
326         if tmp_record['type'] == 'slice' and \
327            not tmp_record['peer_authority']:
328             record = SliceRecord(dict=tmp_record)
329     if not record:
330         raise RecordNotFound(slice_hrn)
331
332     # get sliver info
333     slivers = Slices(api).get_slivers(slice_hrn)
334     if not slivers:
335         raise SliverDoesNotExist(slice_hrn)
336
337     # get initscripts
338     initscripts = []
339     data = {
340         'timestamp': int(time.time()),
341         'initscripts': initscripts,
342         'slivers': slivers
343     }
344
345     # create the ticket
346     object_gid = record.get_gid_object()
347     new_ticket = SfaTicket(subject = object_gid.get_subject())
348     new_ticket.set_gid_caller(api.auth.client_gid)
349     new_ticket.set_gid_object(object_gid)
350     new_ticket.set_issuer(key=api.key, subject=api.hrn)
351     new_ticket.set_pubkey(object_gid.get_pubkey())
352     new_ticket.set_attributes(data)
353     new_ticket.set_rspec(rspec)
354     #new_ticket.set_parent(api.auth.hierarchy.get_auth_ticket(auth_hrn))
355     new_ticket.encode()
356     new_ticket.sign()
357
358     return new_ticket.save_to_string(save_parents=True)
359
360
361
362 def main():
363     api = SfaAPI()
364     """
365     rspec = get_rspec(api, "plc.princeton.sapan", None)
366     #rspec = get_rspec(api, "plc.princeton.coblitz", None)
367     #rspec = get_rspec(api, "plc.pl.sirius", None)
368     print rspec
369     """
370     f = open(sys.argv[1])
371     xml = f.read()
372     f.close()
373     create_slice(api, "plc.princeton.sapan", xml)
374
375 if __name__ == "__main__":
376     main()