* tried to put some sense in the way things get logged, at least on server-side for now
[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
25
26 def __get_registry_objects(slice_xrn, creds, users):
27     """
28
29     """
30     hrn, type = urn_to_hrn(slice_xrn)
31
32     hrn_auth = get_authority(hrn)
33
34     # Build up objects that an SFA registry would return if SFA
35     # could contact the slice's registry directly
36     reg_objects = None
37
38     if users:
39         # dont allow special characters in the site login base
40         only_alphanumeric = re.compile('[^a-zA-Z0-9]+')
41         login_base = only_alphanumeric.sub('', hrn_auth[:20]).lower()
42   
43         reg_objects = {}
44
45         site = {}
46         site['site_id'] = 0
47         site['name'] = 'geni.%s' % login_base 
48         site['enabled'] = True
49         site['max_slices'] = 100
50
51         # Note:
52         # Is it okay if this login base is the same as one already at this myplc site?
53         # Do we need uniqueness?  Should use hrn_auth instead of just the leaf perhaps?
54         site['login_base'] = login_base
55         site['abbreviated_name'] = login_base
56         site['max_slivers'] = 1000
57         reg_objects['site'] = site
58
59         slice = {}
60         slice['expires'] = int(time.mktime(Credential(string=creds[0]).get_lifetime().timetuple()))
61         slice['hrn'] = hrn
62         slice['name'] = site['login_base'] + "_" +  get_leaf(hrn)
63         slice['url'] = hrn
64         slice['description'] = hrn
65         slice['pointer'] = 0
66         reg_objects['slice_record'] = slice
67
68         reg_objects['users'] = {}
69         for user in users:
70             user['key_ids'] = []
71             hrn, _ = urn_to_hrn(user['urn'])
72             user['email'] = hrn + "@geni.net"
73             user['first_name'] = hrn
74             user['last_name'] = hrn
75             reg_objects['users'][user['email']] = user
76
77         return reg_objects
78
79 def __get_hostnames(nodes):
80     hostnames = []
81     for node in nodes:
82         hostnames.append(node.hostname)
83     return hostnames
84
85 def get_version():
86     version = {}
87     version['geni_api'] = 1
88     version['sfa'] = 1
89     return version
90
91 def slice_status(api, slice_xrn, creds):
92     result = {}
93     result['geni_urn'] = slice_xrn
94     result['geni_status'] = 'unknown'
95     result['geni_resources'] = {}
96     return result
97
98 def create_slice(api, slice_xrn, creds, rspec, users):
99     """
100     Create the sliver[s] (slice) at this aggregate.    
101     Verify HRN and initialize the slice record in PLC if necessary.
102     """
103
104     reg_objects = __get_registry_objects(slice_xrn, creds, users)
105
106     hrn, type = urn_to_hrn(slice_xrn)
107     peer = None
108     slices = Slices(api)
109     peer = slices.get_peer(hrn)
110     sfa_peer = slices.get_sfa_peer(hrn)
111     registry = api.registries[api.hrn]
112     credential = api.getCredential()
113     site_id, remote_site_id = slices.verify_site(registry, credential, hrn, 
114                                                  peer, sfa_peer, reg_objects)
115
116     slice_record = slices.verify_slice(registry, credential, hrn, site_id, 
117                                 remote_site_id, peer, sfa_peer, reg_objects)
118      
119     network = Network(api)
120
121     slice = network.get_slice(api, hrn)
122     slice.peer_id = slice_record['peer_slice_id']
123     current = __get_hostnames(slice.get_nodes())
124     
125     network.addRSpec(rspec, api.config.SFA_AGGREGATE_RSPEC_SCHEMA)
126     request = __get_hostnames(network.nodesWithSlivers())
127     
128     # remove nodes not in rspec
129     deleted_nodes = list(set(current).difference(request))
130
131     # add nodes from rspec
132     added_nodes = list(set(request).difference(current))
133
134     try:
135         if peer:
136             api.plshell.UnBindObjectFromPeer(api.plauth, 'slice', slice.id, peer)
137
138         api.plshell.AddSliceToNodes(api.plauth, slice.name, added_nodes) 
139         api.plshell.DeleteSliceFromNodes(api.plauth, slice.name, deleted_nodes)
140
141         network.updateSliceTags()
142
143     finally:
144         if peer:
145             api.plshell.BindObjectToPeer(api.plauth, 'slice', slice.id, peer, 
146                                          slice.peer_id)
147
148     # print network.toxml()
149
150     return True
151
152
153 def renew_slice(api, xrn, creds, exipration_time):
154     hrn, type = urn_to_hrn(xrn)
155     slicename = hrn_to_pl_slicename(hrn)
156     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
157     if not slices:
158         raise RecordNotFound(hrn)
159     slice = slices[0]
160     slice['expires'] = expiration_time
161     api.plshell.UpdateSlice(api.plauth, slice['slice_id'], slice)
162     return 1         
163
164 def start_slice(api, xrn, creds):
165     hrn, type = urn_to_hrn(xrn)
166     slicename = hrn_to_pl_slicename(hrn)
167     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
168     if not slices:
169         raise RecordNotFound(hrn)
170     slice_id = slices[0]['slice_id']
171     slice_tags = api.plshell.GetSliceTags(api.plauth, {'slice_id': slice_id, 'tagname': 'enabled'}, ['slice_tag_id'])
172     # just remove the tag if it exists
173     if slice_tags:
174         api.plshell.DeleteSliceTag(api.plauth, slice_tags[0]['slice_tag_id'])
175
176     return 1
177  
178 def stop_slice(api, xrn, creds):
179     hrn, type = urn_to_hrn(xrn)
180     slicename = hrn_to_pl_slicename(hrn)
181     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
182     if not slices:
183         raise RecordNotFound(hrn)
184     slice_id = slices[0]['slice_id']
185     slice_tags = api.plshell.GetSliceTags(api.plauth, {'slice_id': slice_id, 'tagname': 'enabled'})
186     if not slice_tags:
187         api.plshell.AddSliceTag(api.plauth, slice_id, 'enabled', '0')
188     elif slice_tags[0]['value'] != "0":
189         tag_id = attributes[0]['slice_tag_id']
190         api.plshell.UpdateSliceTag(api.plauth, tag_id, '0')
191     return 1
192
193 def reset_slice(api, xrn):
194     # XX not implemented at this interface
195     return 1
196
197 def delete_slice(api, xrn, creds):
198     hrn, type = urn_to_hrn(xrn)
199     slicename = hrn_to_pl_slicename(hrn)
200     slices = api.plshell.GetSlices(api.plauth, {'name': slicename})
201     if not slices:
202         return 1
203     slice = slices[0]
204
205     # determine if this is a peer slice
206     peer = peers.get_peer(api, hrn)
207     try:
208         if peer:
209             api.plshell.UnBindObjectFromPeer(api.plauth, 'slice', slice['slice_id'], peer)
210         api.plshell.DeleteSliceFromNodes(api.plauth, slicename, slice['node_ids'])
211     finally:
212         if peer:
213             api.plshell.BindObjectToPeer(api.plauth, 'slice', slice['slice_id'], peer, slice['peer_slice_id'])
214     return 1
215
216 def get_slices(api, creds):
217     # look in cache first
218     if api.cache:
219         slices = api.cache.get('slices')
220         if slices:
221             return slices
222
223     # get data from db 
224     slices = api.plshell.GetSlices(api.plauth, {'peer_id': None}, ['name'])
225     slice_hrns = [slicename_to_hrn(api.hrn, slice['name']) for slice in slices]
226     slice_urns = [hrn_to_urn(slice_hrn, 'slice') for slice_hrn in slice_hrns]
227
228     # cache the result
229     if api.cache:
230         api.cache.add('slices', slice_urns) 
231
232     return slice_urns
233     
234 def get_rspec(api, creds, options):
235     # get slice's hrn from options
236     xrn = options.get('geni_slice_urn', None)
237     hrn, type = urn_to_hrn(xrn)
238
239     # look in cache first
240     if api.cache and not xrn:
241         rspec = api.cache.get('nodes')
242         if rspec:
243             return rspec 
244
245     network = Network(api)
246     if (hrn):
247         if network.get_slice(api, hrn):
248             network.addSlice()
249
250     rspec = network.toxml()
251
252     # cache the result
253     if api.cache and not xrn:
254         api.cache.add('nodes', rspec)
255
256     return rspec
257
258
259 def get_ticket(api, xrn, creds, rspec, users):
260
261     reg_objects = __get_registry_objects(xrn, creds, users)
262
263     slice_hrn, type = urn_to_hrn(xrn)
264     slices = Slices(api)
265     peer = slices.get_peer(slice_hrn)
266     sfa_peer = slices.get_sfa_peer(slice_hrn)
267
268     # get the slice record
269     registry = api.registries[api.hrn]
270     credential = api.getCredential()
271     records = registry.Resolve(xrn, credential)
272
273     # similar to create_slice, we must verify that the required records exist
274     # at this aggregate before we can issue a ticket
275     site_id, remote_site_id = slices.verify_site(registry, credential, slice_hrn,
276                                                  peer, sfa_peer, reg_objects)
277     slice = slices.verify_slice(registry, credential, slice_hrn, site_id,
278                                 remote_site_id, peer, sfa_peer, reg_objects)
279
280     # make sure we get a local slice record
281     record = None
282     for tmp_record in records:
283         if tmp_record['type'] == 'slice' and \
284            not tmp_record['peer_authority']:
285             record = SliceRecord(dict=tmp_record)
286     if not record:
287         raise RecordNotFound(slice_hrn)
288
289     # get sliver info
290     slivers = Slices(api).get_slivers(slice_hrn)
291     if not slivers:
292         raise SliverDoesNotExist(slice_hrn)
293
294     # get initscripts
295     initscripts = []
296     data = {
297         'timestamp': int(time.time()),
298         'initscripts': initscripts,
299         'slivers': slivers
300     }
301
302     # create the ticket
303     object_gid = record.get_gid_object()
304     new_ticket = SfaTicket(subject = object_gid.get_subject())
305     new_ticket.set_gid_caller(api.auth.client_gid)
306     new_ticket.set_gid_object(object_gid)
307     new_ticket.set_issuer(key=api.key, subject=api.hrn)
308     new_ticket.set_pubkey(object_gid.get_pubkey())
309     new_ticket.set_attributes(data)
310     new_ticket.set_rspec(rspec)
311     #new_ticket.set_parent(api.auth.hierarchy.get_auth_ticket(auth_hrn))
312     new_ticket.encode()
313     new_ticket.sign()
314
315     return new_ticket.save_to_string(save_parents=True)
316
317
318
319 def main():
320     api = SfaAPI()
321     """
322     rspec = get_rspec(api, "plc.princeton.sapan", None)
323     #rspec = get_rspec(api, "plc.princeton.coblitz", None)
324     #rspec = get_rspec(api, "plc.pl.sirius", None)
325     print rspec
326     """
327     f = open(sys.argv[1])
328     xml = f.read()
329     f.close()
330     create_slice(api, "plc.princeton.sapan", xml)
331
332 if __name__ == "__main__":
333     main()