3a15b68cfabea4d595efa6eb4a93a18048105122
[sfa.git] / sfa / managers / slice_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 from copy import deepcopy
9 from lxml import etree
10 from StringIO import StringIO
11 from types import StringTypes
12
13 from sfa.util.namespace import *
14 from sfa.util.rspec import *
15 from sfa.util.specdict import *
16 from sfa.util.faults import *
17 from sfa.util.record import SfaRecord
18 from sfa.util.policy import Policy
19 from sfa.util.prefixTree import prefixTree
20 from sfa.util.rspec import *
21 from sfa.util.sfaticket import *
22 from sfa.util.debug import log
23 from sfa.util.sfalogging import logger
24 import sfa.plc.peers as peers
25
26
27 def delete_slice(api, xrn, origin_hrn=None):
28     credential = api.getCredential()
29     aggregates = api.aggregates
30     for aggregate in aggregates:
31         success = False
32         # request hash is optional so lets try the call without it
33         try:
34             aggregates[aggregate].delete_slice(credential, xrn, origin_hrn)
35             success = True
36         except:
37             print >> log, "%s" % (traceback.format_exc())
38             print >> log, "Error calling delete slice at aggregate %s" % aggregate
39     return 1
40
41 def create_slice(api, xrn, rspec, origin_hrn=None):
42     hrn, type = urn_to_hrn(xrn)
43
44     # Validate the RSpec against PlanetLab's schema --disabled for now
45     # The schema used here needs to aggregate the PL and VINI schemas
46     # schema = "/var/www/html/schemas/pl.rng"
47     schema = None
48     if schema:
49         try:
50             tree = etree.parse(StringIO(rspec))
51         except etree.XMLSyntaxError:
52             message = str(sys.exc_info()[1])
53             raise InvalidRSpec(message)
54
55         relaxng_doc = etree.parse(schema)
56         relaxng = etree.RelaxNG(relaxng_doc)
57         
58         if not relaxng(tree):
59             error = relaxng.error_log.last_error
60             message = "%s (line %s)" % (error.message, error.line)
61             raise InvalidRSpec(message)
62
63     aggs = api.aggregates
64     cred = api.getCredential()                                                 
65     for agg in aggs:
66         if agg not in [api.auth.client_cred.get_gid_caller().get_hrn()]:      
67             try:
68                 # Just send entire RSpec to each aggregate
69                 aggs[agg].create_slice(cred, xrn, rspec, origin_hrn)
70             except:
71                 print >> log, "Error creating slice %s at %s" % (hrn, agg)
72                 traceback.print_exc()
73
74     return True
75
76 def get_ticket(api, xrn, rspec, origin_hrn=None):
77     slice_hrn, type = urn_to_hrn(xrn)
78     # get the netspecs contained within the clients rspec
79     client_rspec = RSpec(xml=rspec)
80     netspecs = client_rspec.getDictsByTagName('NetSpec')
81     
82     # create an rspec for each individual rspec 
83     rspecs = {}
84     temp_rspec = RSpec()
85     for netspec in netspecs:
86         net_hrn = netspec['name']
87         resources = {'start_time': 0, 'end_time': 0 , 
88                      'network': {'NetSpec' : netspec}}
89         resourceDict = {'RSpec': resources}
90         temp_rspec.parseDict(resourceDict)
91         rspecs[net_hrn] = temp_rspec.toxml() 
92     
93     # send the rspec to the appropiate aggregate/sm
94     aggregates = api.aggregates
95     credential = api.getCredential()
96     tickets = {}
97     for net_hrn in rspecs:
98         net_urn = urn_to_hrn(net_hrn)     
99         try:
100             # if we are directly connected to the aggregate then we can just
101             # send them the request. if not, then we may be connected to an sm
102             # thats connected to the aggregate
103             if net_hrn in aggregates:
104                 ticket = aggregates[net_hrn].get_ticket(credential, xrn, \
105                             rspecs[net_hrn], origin_hrn)
106                 tickets[net_hrn] = ticket
107             else:
108                 # lets forward this rspec to a sm that knows about the network
109                 for agg in aggregates:
110                     network_found = aggregates[agg].get_aggregates(credential, net_urn)
111                     if network_found:
112                         ticket = aggregates[aggregate].get_ticket(credential, \
113                                         slice_hrn, rspecs[net_hrn], origin_hrn)
114                         tickets[aggregate] = ticket
115         except:
116             print >> log, "Error getting ticket for %(slice_hrn)s at aggregate %(net_hrn)s" % \
117                            locals()
118             
119     # create a new ticket
120     new_ticket = SfaTicket(subject = slice_hrn)
121     new_ticket.set_gid_caller(api.auth.client_gid)
122     new_ticket.set_issuer(key=api.key, subject=api.hrn)
123    
124     tmp_rspec = RSpec()
125     networks = []
126     valid_data = {
127         'timestamp': int(time.time()),
128         'initscripts': [],
129         'slivers': [] 
130     } 
131     # merge data from aggregate ticket into new ticket 
132     for agg_ticket in tickets.values():
133         # get data from this ticket
134         agg_ticket = SfaTicket(string=agg_ticket)
135         attributes = agg_ticket.get_attributes()
136         if attributes.get('initscripts', []) != None:
137             valid_data['initscripts'].extend(attributes.get('initscripts', []))
138         if attributes.get('slivers', []) != None:
139             valid_data['slivers'].extend(attributes.get('slivers', []))
140  
141         # set the object gid
142         object_gid = agg_ticket.get_gid_object()
143         new_ticket.set_gid_object(object_gid)
144         new_ticket.set_pubkey(object_gid.get_pubkey())
145
146         # build the rspec
147         tmp_rspec.parseString(agg_ticket.get_rspec())
148         networks.extend([{'NetSpec': tmp_rspec.getDictsByTagName('NetSpec')}])
149     
150     #new_ticket.set_parent(api.auth.hierarchy.get_auth_ticket(auth_hrn))
151     new_ticket.set_attributes(valid_data)
152     resources = {'networks': networks, 'start_time': 0, 'duration': 0}
153     resourceDict = {'RSpec': resources}
154     tmp_rspec.parseDict(resourceDict)
155     new_ticket.set_rspec(tmp_rspec.toxml())
156     new_ticket.encode()
157     new_ticket.sign()          
158     return new_ticket.save_to_string(save_parents=True)
159
160 def start_slice(api, xrn):
161     hrn, type = urn_to_hrn(xrn)
162     slicename = hrn_to_pl_slicename(hrn)
163     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
164     if not slices:
165         raise RecordNotFound(hrn)
166     slice_id = slices[0]
167     attributes = api.plshell.GetSliceTags(api.plauth, {'slice_id': slice_id, 'name': 'enabled'}, ['slice_attribute_id'])
168     attribute_id = attreibutes[0]['slice_attribute_id']
169     api.plshell.UpdateSliceTag(api.plauth, attribute_id, "1" )
170
171     return 1
172  
173 def stop_slice(api, xrn):
174     hrn, type = urn_to_hrn(xrn)
175     slicename = hrn_to_pl_slicename(hrn)
176     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
177     if not slices:
178         raise RecordNotFound(hrn)
179     slice_id = slices[0]['slice_id']
180     attributes = api.plshell.GetSliceTags(api.plauth, {'slice_id': slice_id, 'name': 'enabled'}, ['slice_attribute_id'])
181     attribute_id = attributes[0]['slice_attribute_id']
182     api.plshell.UpdateSliceTag(api.plauth, attribute_id, "0")
183     return 1
184
185 def reset_slice(api, xrn):
186     # XX not implemented at this interface
187     return 1
188
189 def get_slices(api):
190     # XX just import the legacy module and excute that until
191     # we transition the code to this module
192     from sfa.plc.slices import Slices
193     slices = Slices(api)
194     slices.refresh()
195     return [hrn_to_urn(slice_hrn, 'slice') for slice_hrn in slices['hrn']]
196      
197 def get_rspec(api, xrn=None, origin_hrn=None):
198     hrn, type = urn_to_hrn(xrn)
199     rspec = None
200
201     aggs = api.aggregates
202     cred = api.getCredential()                                                 
203
204     print >> log, "Aggregates = %s" % aggs
205     for agg in aggs:
206         if agg not in [api.auth.client_cred.get_gid_caller().get_hrn()]:      
207             try:
208                 # get the rspec from the aggregate
209                 agg_rspec = aggs[agg].get_resources(cred, xrn, origin_hrn)
210             except:
211                 
212                 # XX print out to some error log
213                 print >> log, "Error getting resources at aggregate %s" % agg
214                 traceback.print_exc(log)
215                 print >> log, "%s" % (traceback.format_exc())
216                 continue
217                 
218             try:
219                 tree = etree.parse(StringIO(agg_rspec))
220             except etree.XMLSyntaxError:
221                 message = agg + ": " + str(sys.exc_info()[1])
222                 raise InvalidRSpec(message)
223
224             root = tree.getroot()
225             if root.get("type") in ["SFA"]:
226                 if rspec == None:
227                     rspec = root
228                 else:
229                     for network in root.iterfind("./network"):
230                         rspec.append(deepcopy(network))
231                     for request in root.iterfind("./request"):
232                         rspec.append(deepcopy(request))
233
234     return etree.tostring(rspec, xml_declaration=True, pretty_print=True)
235
236 """
237 Returns the request context required by sfatables. At some point, this
238 mechanism should be changed to refer to "contexts", which is the
239 information that sfatables is requesting. But for now, we just return
240 the basic information needed in a dict.
241 """
242 def fetch_context(slice_hrn, user_hrn, contexts):
243     #slice_hrn = urn_to_hrn(slice_xrn)[0]
244     #user_hrn = urn_to_hrn(user_xrn)[0]
245     base_context = {'sfa':{'user':{'hrn':user_hrn}, 'slice':{'hrn':slice_hrn}}}
246     return base_context
247
248 def main():
249     r = RSpec()
250     r.parseFile(sys.argv[1])
251     rspec = r.toDict()
252     create_slice(None,'plc.princeton.tmacktestslice',rspec)
253
254 if __name__ == "__main__":
255     main()
256