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