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.plc.plslices import PlSlices
19 class AggregateManagerMax (AggregateManager):
21 RSPEC_TMP_FILE_PREFIX = "/tmp/max_rspec"
23 # execute shell command and return both exit code and text output
24 def shell_execute(self, cmd, timeout):
25 pipe = os.popen('{ ' + cmd + '; } 2>&1', 'r')
26 pipe = os.popen(cmd + ' 2>&1', 'r')
34 if code is None: code = 0
35 if text[-1:] == '\n': text = text[:-1]
39 def call_am_apiclient(self, client_app, params, timeout):
41 call AM API client with command like in the following example:
42 cd aggregate_client; java -classpath AggregateWS-client-api.jar:lib/* \
43 net.geni.aggregate.client.examples.CreateSliceNetworkClient \
44 ./repo https://geni:8443/axis2/services/AggregateGENI \
47 (client_path, am_url) = Config().get_max_aggrMgr_info()
48 sys_cmd = "cd " + client_path + "; java -classpath AggregateWS-client-api.jar:lib/* net.geni.aggregate.client.examples." + client_app + " ./repo " + am_url + " " + ' '.join(params)
49 ret = self.shell_execute(sys_cmd, timeout)
50 logger.debug("shell_execute cmd: %s returns %s" % (sys_cmd, ret))
53 # save request RSpec xml content to a tmp file
54 def save_rspec_to_file(self, rspec):
55 path = AggregateManagerMax.RSPEC_TMP_FILE_PREFIX + "_" + \
56 time.strftime('%Y%m%dT%H:%M:%S', time.gmtime(time.time())) +".xml"
57 file = open(path, "w")
62 # get stripped down slice id/name plc.maxpl.xislice1 --> maxpl_xislice1
63 def get_plc_slice_id(self, cred, xrn):
64 (hrn, type) = urn_to_hrn(xrn)
65 slice_id = hrn.find(':')
67 if hrn.find(':') != -1:
69 elif hrn.find('+') != -1:
73 slice_id = hrn.split(sep)[-2] + '_' + hrn.split(sep)[-1]
77 def get_xml_by_tag(self, text, tag):
78 indx1 = text.find('<'+tag)
79 indx2 = text.find('/'+tag+'>')
81 if indx1!=-1 and indx2>indx1:
82 xml = text[indx1:indx2+len(tag)+2]
85 # formerly in aggregate_manager.py but got unused in there...
86 def _get_registry_objects(self, slice_xrn, creds, users):
90 hrn, _ = urn_to_hrn(slice_xrn)
92 #hrn_auth = get_authority(hrn)
94 # Build up objects that an SFA registry would return if SFA
95 # could contact the slice's registry directly
99 # dont allow special characters in the site login base
100 #only_alphanumeric = re.compile('[^a-zA-Z0-9]+')
101 #login_base = only_alphanumeric.sub('', hrn_auth[:20]).lower()
102 slicename = hrn_to_pl_slicename(hrn)
103 login_base = slicename.split('_')[0]
107 site['name'] = 'geni.%s' % login_base
108 site['enabled'] = True
109 site['max_slices'] = 100
112 # Is it okay if this login base is the same as one already at this myplc site?
113 # Do we need uniqueness? Should use hrn_auth instead of just the leaf perhaps?
114 site['login_base'] = login_base
115 site['abbreviated_name'] = login_base
116 site['max_slivers'] = 1000
117 reg_objects['site'] = site
121 # get_expiration always returns a normalized datetime - no need to utcparse
122 extime = Credential(string=creds[0]).get_expiration()
123 # If the expiration time is > 60 days from now, set the expiration time to 60 days from now
124 if extime > datetime.datetime.utcnow() + datetime.timedelta(days=60):
125 extime = datetime.datetime.utcnow() + datetime.timedelta(days=60)
126 slice['expires'] = int(time.mktime(extime.timetuple()))
128 slice['name'] = hrn_to_pl_slicename(hrn)
130 slice['description'] = hrn
132 reg_objects['slice_record'] = slice
134 reg_objects['users'] = {}
137 hrn, _ = urn_to_hrn(user['urn'])
138 user['email'] = hrn_to_pl_slicename(hrn) + "@geni.net"
139 user['first_name'] = hrn
140 user['last_name'] = hrn
141 reg_objects['users'][user['email']] = user
145 def prepare_slice(self, api, slice_xrn, creds, users):
146 reg_objects = self._get_registry_objects(slice_xrn, creds, users)
147 (hrn, type) = urn_to_hrn(slice_xrn)
148 slices = PlSlices(api)
149 peer = slices.get_peer(hrn)
150 sfa_peer = slices.get_sfa_peer(hrn)
153 slice_record = users[0].get('slice_record', {})
154 registry = api.registries[api.hrn]
155 credential = api.getCredential()
156 # ensure site record exists
157 site = slices.verify_site(hrn, slice_record, peer, sfa_peer)
158 # ensure slice record exists
159 slice = slices.verify_slice(hrn, slice_record, peer, sfa_peer)
160 # ensure person records exists
161 persons = slices.verify_persons(hrn, slice, users, peer, sfa_peer)
163 def parse_resources(self, text, slice_xrn):
165 urn = hrn_to_urn(slice_xrn, 'sliver')
166 plc_slice = re.search("Slice Status => ([^\n]+)", text)
167 if plc_slice.group(1) != 'NONE':
169 res['geni_urn'] = urn + '_plc_slice'
170 res['geni_error'] = ''
171 res['geni_status'] = 'unknown'
172 if plc_slice.group(1) == 'CREATED':
173 res['geni_status'] = 'ready'
174 resources.append(res)
175 vlans = re.findall("GRI => ([^\n]+)\n\t Status => ([^\n]+)", text)
178 res['geni_error'] = ''
179 res['geni_urn'] = urn + '_vlan_' + vlan[0]
180 if vlan[1] == 'ACTIVE':
181 res['geni_status'] = 'ready'
182 elif vlan[1] == 'FAILED':
183 res['geni_status'] = 'failed'
185 res['geni_status'] = 'configuring'
186 resources.append(res)
189 def slice_status(self, api, slice_xrn, creds):
190 urn = hrn_to_urn(slice_xrn, 'slice')
192 top_level_status = 'unknown'
193 slice_id = self.get_plc_slice_id(creds, urn)
194 (ret, output) = self.call_am_apiclient("QuerySliceNetworkClient", [slice_id,], 5)
195 # parse output into rspec XML
196 if output.find("Unkown Rspec:") > 0:
197 top_level_staus = 'failed'
198 result['geni_resources'] = ''
202 if output.find("Status => FAILED") > 0:
203 top_level_staus = 'failed'
204 elif ( output.find("Status => ACCEPTED") > 0 or output.find("Status => PENDING") > 0
205 or output.find("Status => INSETUP") > 0 or output.find("Status => INCREATE") > 0
207 top_level_status = 'configuring'
209 top_level_status = 'ready'
210 result['geni_resources'] = self.parse_resources(output, slice_xrn)
211 result['geni_urn'] = urn
212 result['geni_status'] = top_level_status
215 def create_slice(self, api, xrn, cred, rspec, users):
216 indx1 = rspec.find("<RSpec")
217 indx2 = rspec.find("</RSpec>")
218 if indx1 > -1 and indx2 > indx1:
219 rspec = rspec[indx1+len("<RSpec type=\"SFA\">"):indx2-1]
220 rspec_path = self.save_rspec_to_file(rspec)
221 self.prepare_slice(api, xrn, cred, users)
222 slice_id = self.get_plc_slice_id(cred, xrn)
223 sys_cmd = "sed -i \"s/rspec id=\\\"[^\\\"]*/rspec id=\\\"" +slice_id+ "/g\" " + rspec_path + ";sed -i \"s/:rspec=[^:'<\\\" ]*/:rspec=" +slice_id+ "/g\" " + rspec_path
224 ret = self.shell_execute(sys_cmd, 1)
225 sys_cmd = "sed -i \"s/rspec id=\\\"[^\\\"]*/rspec id=\\\"" + rspec_path + "/g\""
226 ret = self.shell_execute(sys_cmd, 1)
227 (ret, output) = self.call_am_apiclient("CreateSliceNetworkClient", [rspec_path,], 3)
229 rspec = "<RSpec type=\"SFA\"> Done! </RSpec>"
232 def delete_slice(self, api, xrn, cred):
233 slice_id = self.get_plc_slice_id(cred, xrn)
234 (ret, output) = self.call_am_apiclient("DeleteSliceNetworkClient", [slice_id,], 3)
239 def get_rspec(self, api, cred, slice_urn):
240 logger.debug("#### called max-get_rspec")
241 #geni_slice_urn: urn:publicid:IDN+plc:maxpl+slice+xi_rspec_test1
242 if slice_urn == None:
243 (ret, output) = self.call_am_apiclient("GetResourceTopology", ['all', '\"\"'], 5)
245 slice_id = self.get_plc_slice_id(cred, slice_urn)
246 (ret, output) = self.call_am_apiclient("GetResourceTopology", ['all', slice_id,], 5)
247 # parse output into rspec XML
248 if output.find("No resouce found") > 0:
249 rspec = "<RSpec type=\"SFA\"> <Fault>No resource found</Fault> </RSpec>"
251 comp_rspec = self.get_xml_by_tag(output, 'computeResource')
252 logger.debug("#### computeResource %s" % comp_rspec)
253 topo_rspec = self.get_xml_by_tag(output, 'topology')
254 logger.debug("#### topology %s" % topo_rspec)
255 rspec = "<RSpec type=\"SFA\"> <network name=\"" + Config().get_interface_hrn() + "\">"
256 if comp_rspec != None:
257 rspec = rspec + self.get_xml_by_tag(output, 'computeResource')
258 if topo_rspec != None:
259 rspec = rspec + self.get_xml_by_tag(output, 'topology')
260 rspec = rspec + "</network> </RSpec>"
263 def start_slice(self, api, xrn, cred):
264 # service not supported
267 def stop_slice(self, api, xrn, cred):
268 # service not supported
271 def reset_slices(self, api, xrn):
272 # service not supported
275 ### GENI AM API Methods
277 def SliverStatus(self, api, slice_xrn, creds, options={}):
278 call_id = options.get('call_id')
279 if Callids().already_handled(call_id): return {}
280 return self.slice_status(api, slice_xrn, creds)
282 def CreateSliver(self, api, slice_xrn, creds, rspec_string, users, options={}):
283 call_id = options.get('call_id')
284 if Callids().already_handled(call_id): return ""
285 #TODO: create real CreateSliver response rspec
286 ret = self.create_slice(api, slice_xrn, creds, rspec_string, users)
288 return self.get_rspec(api, creds, slice_xrn)
290 return "<?xml version=\"1.0\" ?> <RSpec type=\"SFA\"> Error! </RSpec>"
292 def DeleteSliver(self, api, xrn, creds, options={}):
293 call_id = options.get('call_id')
294 if Callids().already_handled(call_id): return ""
295 return self.delete_slice(api, xrn, creds)
298 def ListResources(self, api, creds, options={}):
299 call_id = options.get('call_id')
300 if Callids().already_handled(call_id): return ""
301 # version_string = "rspec_%s" % (rspec_version.get_version_name())
302 slice_urn = options.get('geni_slice_urn')
303 return self.get_rspec(api, creds, slice_urn)
305 def fetch_context(self, slice_hrn, user_hrn, contexts):
307 Returns the request context required by sfatables. At some point, this mechanism should be changed
308 to refer to "contexts", which is the information that sfatables is requesting. But for now, we just
309 return the basic information needed in a dict.
311 base_context = {'sfa':{'user':{'hrn':user_hrn}}}