Bugfix provided by TP 'v' is an Interface object, not ServerProxy, thus doesn't have...
[sfa.git] / sfa / managers / aggregate_manager_max.py
1 import os
2 import time
3 import re
4
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
11
12 # xxx the sfa.rspecs module is dead - this symbol is now undefined
13 #from sfa.rspecs.sfa_rspec import sfa_rspec_version
14
15 from sfa.managers.aggregate_manager import AggregateManager
16
17 from sfa.plc.slices import Slices
18
19 class AggregateManagerMax (AggregateManager):
20
21     RSPEC_TMP_FILE_PREFIX = "/tmp/max_rspec"
22     
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')
27         text = ''
28         while timeout:
29             line = pipe.read()
30             text += line
31             time.sleep(1)
32             timeout = timeout-1
33         code = pipe.close()
34         if code is None: code = 0
35         if text[-1:] == '\n': text = text[:-1]
36         return code, text
37     
38    
39     def call_am_apiclient(self, client_app, params, timeout):
40         """
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 \
45           ... params ...
46         """
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))
51         return ret
52     
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")
58         file.write(rspec)
59         file.close()
60         return path
61     
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(':')
66         sep = '.'
67         if hrn.find(':') != -1:
68             sep=':'
69         elif hrn.find('+') != -1:
70             sep='+'
71         else:
72             sep='.'
73         slice_id = hrn.split(sep)[-2] + '_' + hrn.split(sep)[-1]
74         return slice_id
75     
76     # extract xml 
77     def get_xml_by_tag(self, text, tag):
78         indx1 = text.find('<'+tag)
79         indx2 = text.find('/'+tag+'>')
80         xml = None
81         if indx1!=-1 and indx2>indx1:
82             xml = text[indx1:indx2+len(tag)+2]
83         return xml
84     
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)
88         slices = Slices(api)
89         peer = slices.get_peer(hrn)
90         sfa_peer = slices.get_sfa_peer(hrn)
91         slice_record=None
92         if users:
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)
102     
103     def parse_resources(self, text, slice_xrn):
104         resources = []
105         urn = hrn_to_urn(slice_xrn, 'sliver')
106         plc_slice = re.search("Slice Status => ([^\n]+)", text)
107         if plc_slice.group(1) != 'NONE':
108             res = {}
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)
116         for vlan in vlans:
117             res = {}
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'
124             else:
125                 res['geni_status'] = 'configuring'
126             resources.append(res)
127         return resources
128     
129     def slice_status(self, api, slice_xrn, creds):
130         urn = hrn_to_urn(slice_xrn, 'slice')
131         result = {}
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'] = ''
139         else:
140             has_failure = 0
141             all_active = 0
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
146                  ):
147                 top_level_status = 'configuring'
148             else:
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
153         return result
154     
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)
168         # parse output ?
169         rspec = "<RSpec type=\"SFA\"> Done! </RSpec>"
170         return True
171     
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)
175         # parse output ?
176         return 1
177     
178     
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)
184         else:
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>"
190         else:
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>"
201         return (rspec)
202     
203     def start_slice(self, api, xrn, cred):
204         # service not supported
205         return None
206     
207     def stop_slice(self, api, xrn, cred):
208         # service not supported
209         return None
210     
211     def reset_slices(self, api, xrn):
212         # service not supported
213         return None
214     
215     ### GENI AM API Methods
216     
217     def GetVersion(self, api):
218         xrn=Xrn(api.hrn)
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',
223                         'testbed':'myplc',
224                         'hrn':xrn.get_hrn(),
225                         'request_rspec_versions': request_rspec_versions,
226                         'ad_rspec_versions': ad_rspec_versions,
227                         'default_ad_rspec': dict(sfa_rspec_version)
228                         }
229         return version_core(version_more)
230     
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)
234     
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)
239         if ret:
240             return self.get_rspec(api, creds, slice_xrn)
241         else:
242             return "<?xml version=\"1.0\" ?> <RSpec type=\"SFA\"> Error! </RSpec>"
243     
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)
247     
248     # no caching
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)
254     
255     def fetch_context(self, slice_hrn, user_hrn, contexts):
256         """
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.
260         """
261         base_context = {'sfa':{'user':{'hrn':user_hrn}}}
262         return base_context
263