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