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
21 class AggregateManagerMax (AggregateManager):
23 def __init__(self, config):
26 RSPEC_TMP_FILE_PREFIX = "/tmp/max_rspec"
28 # execute shell command and return both exit code and text output
29 def shell_execute(self, cmd, timeout):
30 pipe = os.popen('{ ' + cmd + '; } 2>&1', 'r')
31 pipe = os.popen(cmd + ' 2>&1', 'r')
45 def call_am_apiclient(self, client_app, params, timeout):
47 call AM API client with command like in the following example:
48 cd aggregate_client; java -classpath AggregateWS-client-api.jar:lib/* \
49 net.geni.aggregate.client.examples.CreateSliceNetworkClient \
50 ./repo https://geni:8443/axis2/services/AggregateGENI \
53 (client_path, am_url) = Config().get_max_aggrMgr_info()
54 sys_cmd = "cd " + client_path + "; java -classpath AggregateWS-client-api.jar:lib/* net.geni.aggregate.client.examples." + \
55 client_app + " ./repo " + am_url + " " + ' '.join(params)
56 ret = self.shell_execute(sys_cmd, timeout)
57 logger.debug("shell_execute cmd: %s returns %s" % (sys_cmd, ret))
60 # save request RSpec xml content to a tmp file
61 def save_rspec_to_file(self, rspec):
62 path = AggregateManagerMax.RSPEC_TMP_FILE_PREFIX + "_" + \
63 time.strftime(SFATIME_FORMAT, time.gmtime(time.time())) + ".xml"
64 file = open(path, "w")
69 # get stripped down slice id/name plc.maxpl.xislice1 --> maxpl_xislice1
70 def get_plc_slice_id(self, cred, xrn):
71 (hrn, type) = urn_to_hrn(xrn)
72 slice_id = hrn.find(':')
74 if hrn.find(':') != -1:
76 elif hrn.find('+') != -1:
80 slice_id = hrn.split(sep)[-2] + '_' + hrn.split(sep)[-1]
84 def get_xml_by_tag(self, text, tag):
85 indx1 = text.find('<' + tag)
86 indx2 = text.find('/' + tag + '>')
88 if indx1 != -1 and indx2 > indx1:
89 xml = text[indx1:indx2 + len(tag) + 2]
92 # formerly in aggregate_manager.py but got unused in there...
93 def _get_registry_objects(self, slice_xrn, creds, users):
97 hrn, _ = urn_to_hrn(slice_xrn)
99 #hrn_auth = get_authority(hrn)
101 # Build up objects that an SFA registry would return if SFA
102 # could contact the slice's registry directly
106 # dont allow special characters in the site login base
107 #only_alphanumeric = re.compile('[^a-zA-Z0-9]+')
108 #login_base = only_alphanumeric.sub('', hrn_auth[:20]).lower()
109 slicename = hrn_to_pl_slicename(hrn)
110 login_base = slicename.split('_')[0]
114 site['name'] = 'geni.%s' % login_base
115 site['enabled'] = True
116 site['max_slices'] = 100
119 # Is it okay if this login base is the same as one already at this myplc site?
120 # Do we need uniqueness? Should use hrn_auth instead of just the
122 site['login_base'] = login_base
123 site['abbreviated_name'] = login_base
124 site['max_slivers'] = 1000
125 reg_objects['site'] = site
129 # get_expiration always returns a normalized datetime - no need to
131 extime = Credential(string=creds[0]).get_expiration()
132 # If the expiration time is > 60 days from now, set the expiration
133 # time to 60 days from now
134 if extime > datetime.datetime.utcnow() + datetime.timedelta(days=60):
135 extime = datetime.datetime.utcnow() + datetime.timedelta(days=60)
136 slice['expires'] = int(time.mktime(extime.timetuple()))
138 slice['name'] = hrn_to_pl_slicename(hrn)
140 slice['description'] = hrn
142 reg_objects['slice_record'] = slice
144 reg_objects['users'] = {}
147 hrn, _ = urn_to_hrn(user['urn'])
148 user['email'] = hrn_to_pl_slicename(hrn) + "@geni.net"
149 user['first_name'] = hrn
150 user['last_name'] = hrn
151 reg_objects['users'][user['email']] = user
155 def prepare_slice(self, api, slice_xrn, creds, users):
156 reg_objects = self._get_registry_objects(slice_xrn, creds, users)
157 (hrn, type) = urn_to_hrn(slice_xrn)
158 slices = PlSlices(self.driver)
159 peer = slices.get_peer(hrn)
160 sfa_peer = slices.get_sfa_peer(hrn)
163 slice_record = users[0].get('slice_record', {})
164 registry = api.registries[api.hrn]
165 credential = api.getCredential()
166 # ensure site record exists
167 site = slices.verify_site(hrn, slice_record, peer, sfa_peer)
168 # ensure slice record exists
169 slice = slices.verify_slice(hrn, slice_record, peer, sfa_peer)
170 # ensure person records exists
171 persons = slices.verify_persons(hrn, slice, users, peer, sfa_peer)
173 def parse_resources(self, text, slice_xrn):
175 urn = hrn_to_urn(slice_xrn, 'sliver')
176 plc_slice = re.search("Slice Status => ([^\n]+)", text)
177 if plc_slice.group(1) != 'NONE':
179 res['geni_urn'] = urn + '_plc_slice'
180 res['geni_error'] = ''
181 res['geni_status'] = 'unknown'
182 if plc_slice.group(1) == 'CREATED':
183 res['geni_status'] = 'ready'
184 resources.append(res)
185 vlans = re.findall("GRI => ([^\n]+)\n\t Status => ([^\n]+)", text)
188 res['geni_error'] = ''
189 res['geni_urn'] = urn + '_vlan_' + vlan[0]
190 if vlan[1] == 'ACTIVE':
191 res['geni_status'] = 'ready'
192 elif vlan[1] == 'FAILED':
193 res['geni_status'] = 'failed'
195 res['geni_status'] = 'configuring'
196 resources.append(res)
199 def slice_status(self, api, slice_xrn, creds):
200 urn = hrn_to_urn(slice_xrn, 'slice')
202 top_level_status = 'unknown'
203 slice_id = self.get_plc_slice_id(creds, urn)
204 (ret, output) = self.call_am_apiclient(
205 "QuerySliceNetworkClient", [slice_id, ], 5)
206 # parse output into rspec XML
207 if output.find("Unkown Rspec:") > 0:
208 top_level_staus = 'failed'
209 result['geni_resources'] = ''
213 if output.find("Status => FAILED") > 0:
214 top_level_staus = 'failed'
215 elif (output.find("Status => ACCEPTED") > 0 or output.find("Status => PENDING") > 0
216 or output.find("Status => INSETUP") > 0 or output.find("Status => INCREATE") > 0
218 top_level_status = 'configuring'
220 top_level_status = 'ready'
221 result['geni_resources'] = self.parse_resources(output, slice_xrn)
222 result['geni_urn'] = urn
223 result['geni_status'] = top_level_status
226 def create_slice(self, api, xrn, cred, rspec, users):
227 indx1 = rspec.find("<RSpec")
228 indx2 = rspec.find("</RSpec>")
229 if indx1 > -1 and indx2 > indx1:
230 rspec = rspec[indx1 + len("<RSpec type=\"SFA\">"):indx2 - 1]
231 rspec_path = self.save_rspec_to_file(rspec)
232 self.prepare_slice(api, xrn, cred, users)
233 slice_id = self.get_plc_slice_id(cred, xrn)
234 sys_cmd = "sed -i \"s/rspec id=\\\"[^\\\"]*/rspec id=\\\"" + slice_id + "/g\" " + \
236 ";sed -i \"s/:rspec=[^:'<\\\" ]*/:rspec=" + \
237 slice_id + "/g\" " + rspec_path
238 ret = self.shell_execute(sys_cmd, 1)
239 sys_cmd = "sed -i \"s/rspec id=\\\"[^\\\"]*/rspec id=\\\"" + \
241 ret = self.shell_execute(sys_cmd, 1)
242 (ret, output) = self.call_am_apiclient(
243 "CreateSliceNetworkClient", [rspec_path, ], 3)
245 rspec = "<RSpec type=\"SFA\"> Done! </RSpec>"
248 def delete_slice(self, api, xrn, cred):
249 slice_id = self.get_plc_slice_id(cred, xrn)
250 (ret, output) = self.call_am_apiclient(
251 "DeleteSliceNetworkClient", [slice_id, ], 3)
255 def get_rspec(self, api, cred, slice_urn):
256 logger.debug("#### called max-get_rspec")
257 # geni_slice_urn: urn:publicid:IDN+plc:maxpl+slice+xi_rspec_test1
258 if slice_urn == None:
259 (ret, output) = self.call_am_apiclient(
260 "GetResourceTopology", ['all', '\"\"'], 5)
262 slice_id = self.get_plc_slice_id(cred, slice_urn)
263 (ret, output) = self.call_am_apiclient(
264 "GetResourceTopology", ['all', slice_id, ], 5)
265 # parse output into rspec XML
266 if output.find("No resouce found") > 0:
267 rspec = "<RSpec type=\"SFA\"> <Fault>No resource found</Fault> </RSpec>"
269 comp_rspec = self.get_xml_by_tag(output, 'computeResource')
270 logger.debug("#### computeResource %s" % comp_rspec)
271 topo_rspec = self.get_xml_by_tag(output, 'topology')
272 logger.debug("#### topology %s" % topo_rspec)
273 rspec = "<RSpec type=\"SFA\"> <network name=\"" + \
274 Config().get_interface_hrn() + "\">"
275 if comp_rspec != None:
276 rspec = rspec + self.get_xml_by_tag(output, 'computeResource')
277 if topo_rspec != None:
278 rspec = rspec + self.get_xml_by_tag(output, 'topology')
279 rspec = rspec + "</network> </RSpec>"
282 def start_slice(self, api, xrn, cred):
283 # service not supported
286 def stop_slice(self, api, xrn, cred):
287 # service not supported
290 def reset_slices(self, api, xrn):
291 # service not supported
294 # GENI AM API Methods
296 def SliverStatus(self, api, slice_xrn, creds, options):
297 call_id = options.get('call_id')
298 if Callids().already_handled(call_id):
300 return self.slice_status(api, slice_xrn, creds)
302 def CreateSliver(self, api, slice_xrn, creds, rspec_string, users, options):
303 call_id = options.get('call_id')
304 if Callids().already_handled(call_id):
306 # TODO: create real CreateSliver response rspec
307 ret = self.create_slice(api, slice_xrn, creds, rspec_string, users)
309 return self.get_rspec(api, creds, slice_xrn)
311 return "<?xml version=\"1.0\" ?> <RSpec type=\"SFA\"> Error! </RSpec>"
313 def DeleteSliver(self, api, xrn, creds, options):
314 call_id = options.get('call_id')
315 if Callids().already_handled(call_id):
317 return self.delete_slice(api, xrn, creds)
320 def ListResources(self, api, creds, options):
321 call_id = options.get('call_id')
322 if Callids().already_handled(call_id):
324 # version_string = "rspec_%s" % (rspec_version.get_version_name())
325 slice_urn = options.get('geni_slice_urn')
326 return self.get_rspec(api, creds, slice_urn)
328 def fetch_context(self, slice_hrn, user_hrn, contexts):
330 Returns the request context required by sfatables. At some point, this mechanism should be changed
331 to refer to "contexts", which is the information that sfatables is requesting. But for now, we just
332 return the basic information needed in a dict.
334 base_context = {'sfa': {'user': {'hrn': user_hrn}}}