dos2unix
[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.sfatime import utcparse
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, get_authority, Xrn
12 from sfa.util.plxrn import hrn_to_pl_slicename
13
14 from sfa.server.sfaapi import SfaApi
15 from sfa.server.registry import Registries
16 from sfa.rspecs.rspec_version import RSpecVersion
17 from sfa.rspecs.sfa_rspec import sfa_rspec_version
18 from sfa.rspecs.rspec_parser import parse_rspec
19
20 from sfa.managers.aggregate_manager import _get_registry_objects, ListSlices
21
22 from sfa.plc.slices import Slices
23
24
25 RSPEC_TMP_FILE_PREFIX = "/tmp/max_rspec"
26
27 # execute shell command and return both exit code and text output
28 def shell_execute(cmd, timeout):
29     pipe = os.popen('{ ' + cmd + '; } 2>&1', 'r')
30     pipe = os.popen(cmd + ' 2>&1', 'r')
31     text = ''
32     while timeout:
33         line = pipe.read()
34         text += line
35         time.sleep(1)
36         timeout = timeout-1
37     code = pipe.close()
38     if code is None: code = 0
39     if text[-1:] == '\n': text = text[:-1]
40     return code, text
41
42 """
43  call AM API client with command like in the following example:
44  cd aggregate_client; java -classpath AggregateWS-client-api.jar:lib/* \
45       net.geni.aggregate.client.examples.CreateSliceNetworkClient \
46       ./repo https://geni:8443/axis2/services/AggregateGENI \
47       ... params ...
48 """
49
50 def call_am_apiclient(client_app, params, timeout):
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 = shell_execute(sys_cmd, timeout)
54     logger.debug("shell_execute cmd: %s returns %s" % (sys_cmd, ret))
55     return ret
56
57 # save request RSpec xml content to a tmp file
58 def save_rspec_to_file(rspec):
59     path = RSPEC_TMP_FILE_PREFIX + "_" + time.strftime('%Y%m%dT%H:%M:%S', time.gmtime(time.time())) +".xml"
60     file = open(path, "w")
61     file.write(rspec)
62     file.close()
63     return path
64
65 # get stripped down slice id/name plc.maxpl.xislice1 --> maxpl_xislice1
66 def get_plc_slice_id(cred, xrn):
67     (hrn, type) = urn_to_hrn(xrn)
68     slice_id = hrn.find(':')
69     sep = '.'
70     if hrn.find(':') != -1:
71         sep=':'
72     elif hrn.find('+') != -1:
73         sep='+'
74     else:
75         sep='.'
76     slice_id = hrn.split(sep)[-2] + '_' + hrn.split(sep)[-1]
77     return slice_id
78
79 # extract xml 
80 def get_xml_by_tag(text, tag):
81     indx1 = text.find('<'+tag)
82     indx2 = text.find('/'+tag+'>')
83     xml = None
84     if indx1!=-1 and indx2>indx1:
85         xml = text[indx1:indx2+len(tag)+2]
86     return xml
87
88 def prepare_slice(api, slice_xrn, creds, users):
89     reg_objects = _get_registry_objects(slice_xrn, creds, users)
90     (hrn, type) = urn_to_hrn(slice_xrn)
91     slices = Slices(api)
92     peer = slices.get_peer(hrn)
93     sfa_peer = slices.get_sfa_peer(hrn)
94     slice_record=None
95     if users:
96         slice_record = users[0].get('slice_record', {})
97     registry = api.registries[api.hrn]
98     credential = api.getCredential()
99     # ensure site record exists
100     site = slices.verify_site(hrn, slice_record, peer, sfa_peer)
101     # ensure slice record exists
102     slice = slices.verify_slice(hrn, slice_record, peer, sfa_peer)
103     # ensure person records exists
104     persons = slices.verify_persons(hrn, slice, users, peer, sfa_peer)
105
106 def parse_resources(text, slice_xrn):
107     resources = []
108     urn = hrn_to_urn(slice_xrn, 'sliver')
109     plc_slice = re.search("Slice Status => ([^\n]+)", text)
110     if plc_slice.group(1) != 'NONE':
111         res = {}
112         res['geni_urn'] = urn + '_plc_slice'
113         res['geni_error'] = ''
114         res['geni_status'] = 'unknown'
115         if plc_slice.group(1) == 'CREATED':
116             res['geni_status'] = 'ready'
117         resources.append(res)
118     vlans = re.findall("GRI => ([^\n]+)\n\t  Status => ([^\n]+)", text)
119     for vlan in vlans:
120         res = {}
121         res['geni_error'] = ''
122         res['geni_urn'] = urn + '_vlan_' + vlan[0]
123         if vlan[1] == 'ACTIVE':
124             res['geni_status'] = 'ready'
125         elif vlan[1] == 'FAILED':
126             res['geni_status'] = 'failed'
127         else:
128             res['geni_status'] = 'configuring'
129         resources.append(res)
130     return resources
131
132 def slice_status(api, slice_xrn, creds):
133     urn = hrn_to_urn(slice_xrn, 'slice')
134     result = {}
135     top_level_status = 'unknown'
136     slice_id = get_plc_slice_id(creds, urn)
137     (ret, output) = call_am_apiclient("QuerySliceNetworkClient", [slice_id,], 5)
138     # parse output into rspec XML
139     if output.find("Unkown Rspec:") > 0:
140         top_level_staus = 'failed'
141         result['geni_resources'] = ''
142     else:
143         has_failure = 0
144         all_active = 0
145         if output.find("Status => FAILED") > 0:
146             top_level_staus = 'failed'
147         elif (    output.find("Status => ACCEPTED") > 0 or output.find("Status => PENDING") > 0
148                or output.find("Status => INSETUP") > 0 or output.find("Status => INCREATE") > 0
149              ):
150             top_level_status = 'configuring'
151         else:
152             top_level_status = 'ready'
153         result['geni_resources'] = parse_resources(output, slice_xrn)
154     result['geni_urn'] = urn
155     result['geni_status'] = top_level_status
156     return result
157
158 def create_slice(api, xrn, cred, rspec, users):
159     indx1 = rspec.find("<RSpec")
160     indx2 = rspec.find("</RSpec>")
161     if indx1 > -1 and indx2 > indx1:
162         rspec = rspec[indx1+len("<RSpec type=\"SFA\">"):indx2-1]
163     rspec_path = save_rspec_to_file(rspec)
164     prepare_slice(api, xrn, cred, users)
165     slice_id = get_plc_slice_id(cred, xrn)
166     sys_cmd = "sed -i \"s/rspec id=\\\"[^\\\"]*/rspec id=\\\"" +slice_id+ "/g\" " + rspec_path + ";sed -i \"s/:rspec=[^:'<\\\" ]*/:rspec=" +slice_id+ "/g\" " + rspec_path
167     ret = shell_execute(sys_cmd, 1)
168     sys_cmd = "sed -i \"s/rspec id=\\\"[^\\\"]*/rspec id=\\\"" + rspec_path + "/g\""
169     ret = shell_execute(sys_cmd, 1)
170     (ret, output) = call_am_apiclient("CreateSliceNetworkClient", [rspec_path,], 3)
171     # parse output ?
172     rspec = "<RSpec type=\"SFA\"> Done! </RSpec>"
173     return True
174
175 def delete_slice(api, xrn, cred):
176     slice_id = get_plc_slice_id(cred, xrn)
177     (ret, output) = call_am_apiclient("DeleteSliceNetworkClient", [slice_id,], 3)
178     # parse output ?
179     return 1
180
181
182 def get_rspec(api, cred, slice_urn):
183     logger.debug("#### called max-get_rspec")
184     #geni_slice_urn: urn:publicid:IDN+plc:maxpl+slice+xi_rspec_test1
185     if slice_urn == None:
186         (ret, output) = call_am_apiclient("GetResourceTopology", ['all', '\"\"'], 5)
187     else:
188         slice_id = get_plc_slice_id(cred, slice_urn)
189         (ret, output) = call_am_apiclient("GetResourceTopology", ['all', slice_id,], 5)
190     # parse output into rspec XML
191     if output.find("No resouce found") > 0:
192         rspec = "<RSpec type=\"SFA\"> <Fault>No resource found</Fault> </RSpec>"
193     else:
194         comp_rspec = get_xml_by_tag(output, 'computeResource')
195         logger.debug("#### computeResource %s" % comp_rspec)
196         topo_rspec = get_xml_by_tag(output, 'topology')
197         logger.debug("#### topology %s" % topo_rspec)
198         rspec = "<RSpec type=\"SFA\"> <network name=\"" + Config().get_interface_hrn() + "\">";
199         if comp_rspec != None:
200             rspec = rspec + get_xml_by_tag(output, 'computeResource')
201         if topo_rspec != None:
202             rspec = rspec + get_xml_by_tag(output, 'topology')
203         rspec = rspec + "</network> </RSpec>"
204     return (rspec)
205
206 def start_slice(api, xrn, cred):
207     # service not supported
208     return None
209
210 def stop_slice(api, xrn, cred):
211     # service not supported
212     return None
213
214 def reset_slices(api, xrn):
215     # service not supported
216     return None
217
218 """
219     GENI AM API Methods
220 """
221
222 def GetVersion(api):
223     xrn=Xrn(api.hrn)
224     request_rspec_versions = [dict(sfa_rspec_version)]
225     ad_rspec_versions = [dict(sfa_rspec_version)]
226     #TODO: MAX-AM specific
227     version_more = {'interface':'aggregate',
228                     'testbed':'myplc',
229                     'hrn':xrn.get_hrn(),
230                     'request_rspec_versions': request_rspec_versions,
231                     'ad_rspec_versions': ad_rspec_versions,
232                     'default_ad_rspec': dict(sfa_rspec_version)
233                     }
234     return version_core(version_more)
235
236 def SliverStatus(api, slice_xrn, creds, call_id):
237     if Callids().already_handled(call_id): return {}
238     return slice_status(api, slice_xrn, creds)
239
240 def CreateSliver(api, slice_xrn, creds, rspec_string, users, call_id):
241     if Callids().already_handled(call_id): return ""
242     #TODO: create real CreateSliver response rspec
243     ret = create_slice(api, slice_xrn, creds, rspec_string, users)
244     if ret:
245         return get_rspec(api, creds, slice_xrn)
246     else:
247         return "<?xml version=\"1.0\" ?> <RSpec type=\"SFA\"> Error! </RSpec>"
248
249 def DeleteSliver(api, xrn, creds, call_id):
250     if Callids().already_handled(call_id): return ""
251     return delete_slice(api, xrn, creds)
252
253 # no caching
254 def ListResources(api, creds, options,call_id):
255     if Callids().already_handled(call_id): return ""
256     # version_string = "rspec_%s" % (rspec_version.get_version_name())
257     slice_urn = options.get('geni_slice_urn')
258     return get_rspec(api, creds, slice_urn)
259
260 def fetch_context(slice_hrn, user_hrn, contexts):
261     """
262     Returns the request context required by sfatables. At some point, this mechanism should be changed
263     to refer to "contexts", which is the information that sfatables is requesting. But for now, we just
264     return the basic information needed in a dict.
265     """
266     base_context = {'sfa':{'user':{'hrn':user_hrn}}}
267     return base_context
268     api = SfaApi()
269     create_slice(api, "plc.maxpl.test000", None, rspec_xml, None)
270