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.slices import Slices
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 def prepare_slice(self, api, slice_xrn, creds, users):
86 reg_objects = self._get_registry_objects(slice_xrn, creds, users)
87 (hrn, type) = urn_to_hrn(slice_xrn)
89 peer = slices.get_peer(hrn)
90 sfa_peer = slices.get_sfa_peer(hrn)
93 slice_record = users[0].get('slice_record', {})
94 registry = api.registries[api.hrn]
95 credential = api.getCredential()
96 # ensure site record exists
97 site = slices.verify_site(hrn, slice_record, peer, sfa_peer)
98 # ensure slice record exists
99 slice = slices.verify_slice(hrn, slice_record, peer, sfa_peer)
100 # ensure person records exists
101 persons = slices.verify_persons(hrn, slice, users, peer, sfa_peer)
103 def parse_resources(self, text, slice_xrn):
105 urn = hrn_to_urn(slice_xrn, 'sliver')
106 plc_slice = re.search("Slice Status => ([^\n]+)", text)
107 if plc_slice.group(1) != 'NONE':
109 res['geni_urn'] = urn + '_plc_slice'
110 res['geni_error'] = ''
111 res['geni_status'] = 'unknown'
112 if plc_slice.group(1) == 'CREATED':
113 res['geni_status'] = 'ready'
114 resources.append(res)
115 vlans = re.findall("GRI => ([^\n]+)\n\t Status => ([^\n]+)", text)
118 res['geni_error'] = ''
119 res['geni_urn'] = urn + '_vlan_' + vlan[0]
120 if vlan[1] == 'ACTIVE':
121 res['geni_status'] = 'ready'
122 elif vlan[1] == 'FAILED':
123 res['geni_status'] = 'failed'
125 res['geni_status'] = 'configuring'
126 resources.append(res)
129 def slice_status(self, api, slice_xrn, creds):
130 urn = hrn_to_urn(slice_xrn, 'slice')
132 top_level_status = 'unknown'
133 slice_id = self.get_plc_slice_id(creds, urn)
134 (ret, output) = self.call_am_apiclient("QuerySliceNetworkClient", [slice_id,], 5)
135 # parse output into rspec XML
136 if output.find("Unkown Rspec:") > 0:
137 top_level_staus = 'failed'
138 result['geni_resources'] = ''
142 if output.find("Status => FAILED") > 0:
143 top_level_staus = 'failed'
144 elif ( output.find("Status => ACCEPTED") > 0 or output.find("Status => PENDING") > 0
145 or output.find("Status => INSETUP") > 0 or output.find("Status => INCREATE") > 0
147 top_level_status = 'configuring'
149 top_level_status = 'ready'
150 result['geni_resources'] = self.parse_resources(output, slice_xrn)
151 result['geni_urn'] = urn
152 result['geni_status'] = top_level_status
155 def create_slice(self, api, xrn, cred, rspec, users):
156 indx1 = rspec.find("<RSpec")
157 indx2 = rspec.find("</RSpec>")
158 if indx1 > -1 and indx2 > indx1:
159 rspec = rspec[indx1+len("<RSpec type=\"SFA\">"):indx2-1]
160 rspec_path = self.save_rspec_to_file(rspec)
161 self.prepare_slice(api, xrn, cred, users)
162 slice_id = self.get_plc_slice_id(cred, xrn)
163 sys_cmd = "sed -i \"s/rspec id=\\\"[^\\\"]*/rspec id=\\\"" +slice_id+ "/g\" " + rspec_path + ";sed -i \"s/:rspec=[^:'<\\\" ]*/:rspec=" +slice_id+ "/g\" " + rspec_path
164 ret = self.shell_execute(sys_cmd, 1)
165 sys_cmd = "sed -i \"s/rspec id=\\\"[^\\\"]*/rspec id=\\\"" + rspec_path + "/g\""
166 ret = self.shell_execute(sys_cmd, 1)
167 (ret, output) = self.call_am_apiclient("CreateSliceNetworkClient", [rspec_path,], 3)
169 rspec = "<RSpec type=\"SFA\"> Done! </RSpec>"
172 def delete_slice(self, api, xrn, cred):
173 slice_id = self.get_plc_slice_id(cred, xrn)
174 (ret, output) = self.call_am_apiclient("DeleteSliceNetworkClient", [slice_id,], 3)
179 def get_rspec(self, api, cred, slice_urn):
180 logger.debug("#### called max-get_rspec")
181 #geni_slice_urn: urn:publicid:IDN+plc:maxpl+slice+xi_rspec_test1
182 if slice_urn == None:
183 (ret, output) = self.call_am_apiclient("GetResourceTopology", ['all', '\"\"'], 5)
185 slice_id = self.get_plc_slice_id(cred, slice_urn)
186 (ret, output) = self.call_am_apiclient("GetResourceTopology", ['all', slice_id,], 5)
187 # parse output into rspec XML
188 if output.find("No resouce found") > 0:
189 rspec = "<RSpec type=\"SFA\"> <Fault>No resource found</Fault> </RSpec>"
191 comp_rspec = self.get_xml_by_tag(output, 'computeResource')
192 logger.debug("#### computeResource %s" % comp_rspec)
193 topo_rspec = self.get_xml_by_tag(output, 'topology')
194 logger.debug("#### topology %s" % topo_rspec)
195 rspec = "<RSpec type=\"SFA\"> <network name=\"" + Config().get_interface_hrn() + "\">"
196 if comp_rspec != None:
197 rspec = rspec + self.get_xml_by_tag(output, 'computeResource')
198 if topo_rspec != None:
199 rspec = rspec + self.get_xml_by_tag(output, 'topology')
200 rspec = rspec + "</network> </RSpec>"
203 def start_slice(self, api, xrn, cred):
204 # service not supported
207 def stop_slice(self, api, xrn, cred):
208 # service not supported
211 def reset_slices(self, api, xrn):
212 # service not supported
215 ### GENI AM API Methods
217 def GetVersion(self, api):
219 request_rspec_versions = [dict(sfa_rspec_version)]
220 ad_rspec_versions = [dict(sfa_rspec_version)]
221 #TODO: MAX-AM specific
222 version_more = {'interface':'aggregate',
225 'request_rspec_versions': request_rspec_versions,
226 'ad_rspec_versions': ad_rspec_versions,
227 'default_ad_rspec': dict(sfa_rspec_version)
229 return version_core(version_more)
231 def SliverStatus(self, api, slice_xrn, creds, call_id):
232 if Callids().already_handled(call_id): return {}
233 return self.slice_status(api, slice_xrn, creds)
235 def CreateSliver(self, api, slice_xrn, creds, rspec_string, users, call_id):
236 if Callids().already_handled(call_id): return ""
237 #TODO: create real CreateSliver response rspec
238 ret = self.create_slice(api, slice_xrn, creds, rspec_string, users)
240 return self.get_rspec(api, creds, slice_xrn)
242 return "<?xml version=\"1.0\" ?> <RSpec type=\"SFA\"> Error! </RSpec>"
244 def DeleteSliver(self, api, xrn, creds, call_id):
245 if Callids().already_handled(call_id): return ""
246 return self.delete_slice(api, xrn, creds)
249 def ListResources(self, api, creds, options,call_id):
250 if Callids().already_handled(call_id): return ""
251 # version_string = "rspec_%s" % (rspec_version.get_version_name())
252 slice_urn = options.get('geni_slice_urn')
253 return self.get_rspec(api, creds, slice_urn)
255 def fetch_context(self, slice_hrn, user_hrn, contexts):
257 Returns the request context required by sfatables. At some point, this mechanism should be changed
258 to refer to "contexts", which is the information that sfatables is requesting. But for now, we just
259 return the basic information needed in a dict.
261 base_context = {'sfa':{'user':{'hrn':user_hrn}}}