5 #from sfa.util.faults import *
6 from sfa.util.sfalogging import logger
7 from sfa.util.config import Config
8 from sfa.util.callids import Callids
9 from sfa.util.version import version_core
10 from sfa.util.xrn import urn_to_hrn, hrn_to_urn, Xrn
12 # xxx the sfa.rspecs module is dead - this symbol is now undefined
13 #from sfa.rspecs.sfa_rspec import sfa_rspec_version
15 from sfa.managers.aggregate_manager import AggregateManager
17 from sfa.planetlab.plslices import PlSlices
19 class AggregateManagerMax (AggregateManager):
21 def __init__ (self, config):
24 RSPEC_TMP_FILE_PREFIX = "/tmp/max_rspec"
26 # execute shell command and return both exit code and text output
27 def shell_execute(self, cmd, timeout):
28 pipe = os.popen('{ ' + cmd + '; } 2>&1', 'r')
29 pipe = os.popen(cmd + ' 2>&1', 'r')
37 if code is None: code = 0
38 if text[-1:] == '\n': text = text[:-1]
42 def call_am_apiclient(self, client_app, params, timeout):
44 call AM API client with command like in the following example:
45 cd aggregate_client; java -classpath AggregateWS-client-api.jar:lib/* \
46 net.geni.aggregate.client.examples.CreateSliceNetworkClient \
47 ./repo https://geni:8443/axis2/services/AggregateGENI \
50 (client_path, am_url) = Config().get_max_aggrMgr_info()
51 sys_cmd = "cd " + client_path + "; java -classpath AggregateWS-client-api.jar:lib/* net.geni.aggregate.client.examples." + client_app + " ./repo " + am_url + " " + ' '.join(params)
52 ret = self.shell_execute(sys_cmd, timeout)
53 logger.debug("shell_execute cmd: %s returns %s" % (sys_cmd, ret))
56 # save request RSpec xml content to a tmp file
57 def save_rspec_to_file(self, rspec):
58 path = AggregateManagerMax.RSPEC_TMP_FILE_PREFIX + "_" + \
59 time.strftime('%Y%m%dT%H:%M:%S', time.gmtime(time.time())) +".xml"
60 file = open(path, "w")
65 # get stripped down slice id/name plc.maxpl.xislice1 --> maxpl_xislice1
66 def get_plc_slice_id(self, cred, xrn):
67 (hrn, type) = urn_to_hrn(xrn)
68 slice_id = hrn.find(':')
70 if hrn.find(':') != -1:
72 elif hrn.find('+') != -1:
76 slice_id = hrn.split(sep)[-2] + '_' + hrn.split(sep)[-1]
80 def get_xml_by_tag(self, text, tag):
81 indx1 = text.find('<'+tag)
82 indx2 = text.find('/'+tag+'>')
84 if indx1!=-1 and indx2>indx1:
85 xml = text[indx1:indx2+len(tag)+2]
88 # formerly in aggregate_manager.py but got unused in there...
89 def _get_registry_objects(self, slice_xrn, creds, users):
93 hrn, _ = urn_to_hrn(slice_xrn)
95 #hrn_auth = get_authority(hrn)
97 # Build up objects that an SFA registry would return if SFA
98 # could contact the slice's registry directly
102 # dont allow special characters in the site login base
103 #only_alphanumeric = re.compile('[^a-zA-Z0-9]+')
104 #login_base = only_alphanumeric.sub('', hrn_auth[:20]).lower()
105 slicename = hrn_to_pl_slicename(hrn)
106 login_base = slicename.split('_')[0]
110 site['name'] = 'geni.%s' % login_base
111 site['enabled'] = True
112 site['max_slices'] = 100
115 # Is it okay if this login base is the same as one already at this myplc site?
116 # Do we need uniqueness? Should use hrn_auth instead of just the leaf perhaps?
117 site['login_base'] = login_base
118 site['abbreviated_name'] = login_base
119 site['max_slivers'] = 1000
120 reg_objects['site'] = site
124 # get_expiration always returns a normalized datetime - no need to utcparse
125 extime = Credential(string=creds[0]).get_expiration()
126 # If the expiration time is > 60 days from now, set the expiration time to 60 days from now
127 if extime > datetime.datetime.utcnow() + datetime.timedelta(days=60):
128 extime = datetime.datetime.utcnow() + datetime.timedelta(days=60)
129 slice['expires'] = int(time.mktime(extime.timetuple()))
131 slice['name'] = hrn_to_pl_slicename(hrn)
133 slice['description'] = hrn
135 reg_objects['slice_record'] = slice
137 reg_objects['users'] = {}
140 hrn, _ = urn_to_hrn(user['urn'])
141 user['email'] = hrn_to_pl_slicename(hrn) + "@geni.net"
142 user['first_name'] = hrn
143 user['last_name'] = hrn
144 reg_objects['users'][user['email']] = user
148 def prepare_slice(self, api, slice_xrn, creds, users):
149 reg_objects = self._get_registry_objects(slice_xrn, creds, users)
150 (hrn, type) = urn_to_hrn(slice_xrn)
151 slices = PlSlices(self.driver)
152 peer = slices.get_peer(hrn)
153 sfa_peer = slices.get_sfa_peer(hrn)
156 slice_record = users[0].get('slice_record', {})
157 registry = api.registries[api.hrn]
158 credential = api.getCredential()
159 # ensure site record exists
160 site = slices.verify_site(hrn, slice_record, peer, sfa_peer)
161 # ensure slice record exists
162 slice = slices.verify_slice(hrn, slice_record, peer, sfa_peer)
163 # ensure person records exists
164 persons = slices.verify_persons(hrn, slice, users, peer, sfa_peer)
166 def parse_resources(self, text, slice_xrn):
168 urn = hrn_to_urn(slice_xrn, 'sliver')
169 plc_slice = re.search("Slice Status => ([^\n]+)", text)
170 if plc_slice.group(1) != 'NONE':
172 res['geni_urn'] = urn + '_plc_slice'
173 res['geni_error'] = ''
174 res['geni_status'] = 'unknown'
175 if plc_slice.group(1) == 'CREATED':
176 res['geni_status'] = 'ready'
177 resources.append(res)
178 vlans = re.findall("GRI => ([^\n]+)\n\t Status => ([^\n]+)", text)
181 res['geni_error'] = ''
182 res['geni_urn'] = urn + '_vlan_' + vlan[0]
183 if vlan[1] == 'ACTIVE':
184 res['geni_status'] = 'ready'
185 elif vlan[1] == 'FAILED':
186 res['geni_status'] = 'failed'
188 res['geni_status'] = 'configuring'
189 resources.append(res)
192 def slice_status(self, api, slice_xrn, creds):
193 urn = hrn_to_urn(slice_xrn, 'slice')
195 top_level_status = 'unknown'
196 slice_id = self.get_plc_slice_id(creds, urn)
197 (ret, output) = self.call_am_apiclient("QuerySliceNetworkClient", [slice_id,], 5)
198 # parse output into rspec XML
199 if output.find("Unkown Rspec:") > 0:
200 top_level_staus = 'failed'
201 result['geni_resources'] = ''
205 if output.find("Status => FAILED") > 0:
206 top_level_staus = 'failed'
207 elif ( output.find("Status => ACCEPTED") > 0 or output.find("Status => PENDING") > 0
208 or output.find("Status => INSETUP") > 0 or output.find("Status => INCREATE") > 0
210 top_level_status = 'configuring'
212 top_level_status = 'ready'
213 result['geni_resources'] = self.parse_resources(output, slice_xrn)
214 result['geni_urn'] = urn
215 result['geni_status'] = top_level_status
218 def create_slice(self, api, xrn, cred, rspec, users):
219 indx1 = rspec.find("<RSpec")
220 indx2 = rspec.find("</RSpec>")
221 if indx1 > -1 and indx2 > indx1:
222 rspec = rspec[indx1+len("<RSpec type=\"SFA\">"):indx2-1]
223 rspec_path = self.save_rspec_to_file(rspec)
224 self.prepare_slice(api, xrn, cred, users)
225 slice_id = self.get_plc_slice_id(cred, xrn)
226 sys_cmd = "sed -i \"s/rspec id=\\\"[^\\\"]*/rspec id=\\\"" +slice_id+ "/g\" " + rspec_path + ";sed -i \"s/:rspec=[^:'<\\\" ]*/:rspec=" +slice_id+ "/g\" " + rspec_path
227 ret = self.shell_execute(sys_cmd, 1)
228 sys_cmd = "sed -i \"s/rspec id=\\\"[^\\\"]*/rspec id=\\\"" + rspec_path + "/g\""
229 ret = self.shell_execute(sys_cmd, 1)
230 (ret, output) = self.call_am_apiclient("CreateSliceNetworkClient", [rspec_path,], 3)
232 rspec = "<RSpec type=\"SFA\"> Done! </RSpec>"
235 def delete_slice(self, api, xrn, cred):
236 slice_id = self.get_plc_slice_id(cred, xrn)
237 (ret, output) = self.call_am_apiclient("DeleteSliceNetworkClient", [slice_id,], 3)
242 def get_rspec(self, api, cred, slice_urn):
243 logger.debug("#### called max-get_rspec")
244 #geni_slice_urn: urn:publicid:IDN+plc:maxpl+slice+xi_rspec_test1
245 if slice_urn == None:
246 (ret, output) = self.call_am_apiclient("GetResourceTopology", ['all', '\"\"'], 5)
248 slice_id = self.get_plc_slice_id(cred, slice_urn)
249 (ret, output) = self.call_am_apiclient("GetResourceTopology", ['all', slice_id,], 5)
250 # parse output into rspec XML
251 if output.find("No resouce found") > 0:
252 rspec = "<RSpec type=\"SFA\"> <Fault>No resource found</Fault> </RSpec>"
254 comp_rspec = self.get_xml_by_tag(output, 'computeResource')
255 logger.debug("#### computeResource %s" % comp_rspec)
256 topo_rspec = self.get_xml_by_tag(output, 'topology')
257 logger.debug("#### topology %s" % topo_rspec)
258 rspec = "<RSpec type=\"SFA\"> <network name=\"" + Config().get_interface_hrn() + "\">"
259 if comp_rspec != None:
260 rspec = rspec + self.get_xml_by_tag(output, 'computeResource')
261 if topo_rspec != None:
262 rspec = rspec + self.get_xml_by_tag(output, 'topology')
263 rspec = rspec + "</network> </RSpec>"
266 def start_slice(self, api, xrn, cred):
267 # service not supported
270 def stop_slice(self, api, xrn, cred):
271 # service not supported
274 def reset_slices(self, api, xrn):
275 # service not supported
278 ### GENI AM API Methods
280 def SliverStatus(self, api, slice_xrn, creds, options):
281 call_id = options.get('call_id')
282 if Callids().already_handled(call_id): return {}
283 return self.slice_status(api, slice_xrn, creds)
285 def CreateSliver(self, api, slice_xrn, creds, rspec_string, users, options):
286 call_id = options.get('call_id')
287 if Callids().already_handled(call_id): return ""
288 #TODO: create real CreateSliver response rspec
289 ret = self.create_slice(api, slice_xrn, creds, rspec_string, users)
291 return self.get_rspec(api, creds, slice_xrn)
293 return "<?xml version=\"1.0\" ?> <RSpec type=\"SFA\"> Error! </RSpec>"
295 def DeleteSliver(self, api, xrn, creds, options):
296 call_id = options.get('call_id')
297 if Callids().already_handled(call_id): return ""
298 return self.delete_slice(api, xrn, creds)
301 def ListResources(self, api, creds, options):
302 call_id = options.get('call_id')
303 if Callids().already_handled(call_id): return ""
304 # version_string = "rspec_%s" % (rspec_version.get_version_name())
305 slice_urn = options.get('geni_slice_urn')
306 return self.get_rspec(api, creds, slice_urn)
308 def fetch_context(self, slice_hrn, user_hrn, contexts):
310 Returns the request context required by sfatables. At some point, this mechanism should be changed
311 to refer to "contexts", which is the information that sfatables is requesting. But for now, we just
312 return the basic information needed in a dict.
314 base_context = {'sfa':{'user':{'hrn':user_hrn}}}