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