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