one more fix to renewsliver
[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 import re
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.plc.slices import Slices
19 from sfa.trust.credential import Credential
20 import sfa.plc.peers as peers
21 from sfa.plc.network import *
22 from sfa.plc.api import SfaAPI
23 from sfa.plc.slices import *
24 from dateutil.parser import parse
25
26
27 def __get_registry_objects(slice_xrn, creds, users):
28     """
29
30     """
31     hrn, type = urn_to_hrn(slice_xrn)
32
33     hrn_auth = get_authority(hrn)
34
35     # Build up objects that an SFA registry would return if SFA
36     # could contact the slice's registry directly
37     reg_objects = None
38
39     if users:
40         # dont allow special characters in the site login base
41         #only_alphanumeric = re.compile('[^a-zA-Z0-9]+')
42         #login_base = only_alphanumeric.sub('', hrn_auth[:20]).lower()
43         slicename = hrn_to_pl_slicename(hrn)
44         login_base = slicename.split('_')[0]
45         reg_objects = {}
46
47         site = {}
48         site['site_id'] = 0
49         site['name'] = 'geni.%s' % login_base 
50         site['enabled'] = True
51         site['max_slices'] = 100
52
53         # Note:
54         # Is it okay if this login base is the same as one already at this myplc site?
55         # Do we need uniqueness?  Should use hrn_auth instead of just the leaf perhaps?
56         site['login_base'] = login_base
57         site['abbreviated_name'] = login_base
58         site['max_slivers'] = 1000
59         reg_objects['site'] = site
60
61         slice = {}
62         slice['expires'] = int(time.mktime(Credential(string=creds[0]).get_expiration().timetuple()))
63         slice['hrn'] = hrn
64         slice['name'] = hrn_to_pl_slicename(hrn)
65         slice['url'] = hrn
66         slice['description'] = hrn
67         slice['pointer'] = 0
68         reg_objects['slice_record'] = slice
69
70         reg_objects['users'] = {}
71         for user in users:
72             user['key_ids'] = []
73             hrn, _ = urn_to_hrn(user['urn'])
74             user['email'] = hrn_to_pl_slicename(hrn) + "@geni.net"
75             user['first_name'] = hrn
76             user['last_name'] = hrn
77             reg_objects['users'][user['email']] = user
78
79         return reg_objects
80
81 def __get_hostnames(nodes):
82     hostnames = []
83     for node in nodes:
84         hostnames.append(node.hostname)
85     return hostnames
86
87 def get_version():
88     version = {}
89     version['geni_api'] = 1
90     version['sfa'] = 1
91     return version
92
93 def slice_status(api, slice_xrn, creds):
94     hrn, type = urn_to_hrn(slice_xrn)
95     # find out where this slice is currently running
96     api.logger.info(hrn)
97     slicename = hrn_to_pl_slicename(hrn)
98     
99     slices = api.plshell.GetSlices(api.plauth, [slicename], ['node_ids','person_ids','name','expires'])
100     if len(slices) == 0:        
101         raise Exception("Slice %s not found (used %s as slicename internally)" % slice_xrn, slicename)
102     slice = slices[0]
103     
104     nodes = api.plshell.GetNodes(api.plauth, slice['node_ids'],
105                                     ['hostname', 'boot_state', 'last_contact'])
106     api.logger.info(slice)
107     api.logger.info(nodes)
108     
109     result = {}
110     result['geni_urn'] = slice_xrn
111     result['geni_status'] = 'unknown'
112     result['pl_login'] = slice['name']
113     result['pl_expires'] = slice['expires']
114     
115     resources = []
116     
117     for node in nodes:
118         res = {}
119         res['pl_hostname'] = node['hostname']
120         res['pl_boot_state'] = node['boot_state']
121         res['pl_last_contact'] = node['last_contact']
122         res['geni_urn'] = ''
123         res['geni_status'] = 'unknown'
124         res['geni_error'] = ''
125
126         resources.append(res)
127         
128     result['geni_resources'] = resources
129     return result
130
131 def create_slice(api, slice_xrn, creds, rspec, users):
132     """
133     Create the sliver[s] (slice) at this aggregate.    
134     Verify HRN and initialize the slice record in PLC if necessary.
135     """
136
137     reg_objects = __get_registry_objects(slice_xrn, creds, users)
138
139     hrn, type = urn_to_hrn(slice_xrn)
140     peer = None
141     slices = Slices(api)
142     peer = slices.get_peer(hrn)
143     sfa_peer = slices.get_sfa_peer(hrn)
144     registry = api.registries[api.hrn]
145     credential = api.getCredential()
146     site_id, remote_site_id = slices.verify_site(registry, credential, hrn, 
147                                                  peer, sfa_peer, reg_objects)
148
149     slice_record = slices.verify_slice(registry, credential, hrn, site_id, 
150                                 remote_site_id, peer, sfa_peer, reg_objects)
151      
152     network = Network(api)
153
154     slice = network.get_slice(api, hrn)
155     slice.peer_id = slice_record['peer_slice_id']
156     current = __get_hostnames(slice.get_nodes())
157     
158     network.addRSpec(rspec, api.config.SFA_AGGREGATE_RSPEC_SCHEMA)
159     request = __get_hostnames(network.nodesWithSlivers())
160     
161     # remove nodes not in rspec
162     deleted_nodes = list(set(current).difference(request))
163
164     # add nodes from rspec
165     added_nodes = list(set(request).difference(current))
166
167     try:
168         if peer:
169             api.plshell.UnBindObjectFromPeer(api.plauth, 'slice', slice.id, peer)
170
171         api.plshell.AddSliceToNodes(api.plauth, slice.name, added_nodes) 
172         api.plshell.DeleteSliceFromNodes(api.plauth, slice.name, deleted_nodes)
173
174         network.updateSliceTags()
175
176     finally:
177         if peer:
178             api.plshell.BindObjectToPeer(api.plauth, 'slice', slice.id, peer, 
179                                          slice.peer_id)
180
181     # print network.toxml()
182
183     return True
184
185
186 def renew_slice(api, xrn, creds, expiration_time):
187     hrn, type = urn_to_hrn(xrn)
188     slicename = hrn_to_pl_slicename(hrn)
189     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
190     if not slices:
191         raise RecordNotFound(hrn)
192     slice = slices[0]
193     requested_time = parse(expiration_time)
194     slice['expires'] = int(time.mktime(requested_time.timetuple()))
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     # look in cache first
274     if api.cache and not xrn:
275         rspec = api.cache.get('nodes')
276         if rspec:
277             return rspec 
278
279     network = Network(api)
280     if (hrn):
281         if network.get_slice(api, hrn):
282             network.addSlice()
283
284     rspec = network.toxml()
285
286     # cache the result
287     if api.cache and not xrn:
288         api.cache.add('nodes', rspec)
289
290     return rspec
291
292
293 def get_ticket(api, xrn, creds, rspec, users):
294
295     reg_objects = __get_registry_objects(xrn, creds, users)
296
297     slice_hrn, type = urn_to_hrn(xrn)
298     slices = Slices(api)
299     peer = slices.get_peer(slice_hrn)
300     sfa_peer = slices.get_sfa_peer(slice_hrn)
301
302     # get the slice record
303     registry = api.registries[api.hrn]
304     credential = api.getCredential()
305     records = registry.Resolve(xrn, credential)
306
307     # similar to create_slice, we must verify that the required records exist
308     # at this aggregate before we can issue a ticket
309     site_id, remote_site_id = slices.verify_site(registry, credential, slice_hrn,
310                                                  peer, sfa_peer, reg_objects)
311     slice = slices.verify_slice(registry, credential, slice_hrn, site_id,
312                                 remote_site_id, peer, sfa_peer, reg_objects)
313
314     # make sure we get a local slice record
315     record = None
316     for tmp_record in records:
317         if tmp_record['type'] == 'slice' and \
318            not tmp_record['peer_authority']:
319             record = SliceRecord(dict=tmp_record)
320     if not record:
321         raise RecordNotFound(slice_hrn)
322
323     # get sliver info
324     slivers = Slices(api).get_slivers(slice_hrn)
325     if not slivers:
326         raise SliverDoesNotExist(slice_hrn)
327
328     # get initscripts
329     initscripts = []
330     data = {
331         'timestamp': int(time.time()),
332         'initscripts': initscripts,
333         'slivers': slivers
334     }
335
336     # create the ticket
337     object_gid = record.get_gid_object()
338     new_ticket = SfaTicket(subject = object_gid.get_subject())
339     new_ticket.set_gid_caller(api.auth.client_gid)
340     new_ticket.set_gid_object(object_gid)
341     new_ticket.set_issuer(key=api.key, subject=api.hrn)
342     new_ticket.set_pubkey(object_gid.get_pubkey())
343     new_ticket.set_attributes(data)
344     new_ticket.set_rspec(rspec)
345     #new_ticket.set_parent(api.auth.hierarchy.get_auth_ticket(auth_hrn))
346     new_ticket.encode()
347     new_ticket.sign()
348
349     return new_ticket.save_to_string(save_parents=True)
350
351
352
353 def main():
354     api = SfaAPI()
355     """
356     rspec = get_rspec(api, "plc.princeton.sapan", None)
357     #rspec = get_rspec(api, "plc.princeton.coblitz", None)
358     #rspec = get_rspec(api, "plc.pl.sirius", None)
359     print rspec
360     """
361     f = open(sys.argv[1])
362     xml = f.read()
363     f.close()
364     create_slice(api, "plc.princeton.sapan", xml)
365
366 if __name__ == "__main__":
367     main()