REST SFA - output format using Ontology Translator
[myslice.git] / rest / sfa_api.py
1 import os
2 import json
3 import ConfigParser 
4 import datetime
5 from time                           import mktime
6 import time
7 import xmltodict
8
9 from django.shortcuts               import render_to_response
10 from django.http                    import HttpResponse,QueryDict
11
12 from sfa.trust.certificate          import Keypair, Certificate
13 from sfa.client.sfaserverproxy      import SfaServerProxy
14 from manifold.gateways.sfa.proxy    import SFAProxy
15 from sfa.client.return_value        import ReturnValue
16 from sfa.util.xrn                   import Xrn, get_leaf, get_authority, hrn_to_urn, urn_to_hrn
17
18 from manifold.core.query            import Query
19
20 from manifoldapi.manifoldapi        import execute_admin_query
21
22 from unfold.loginrequired           import LoginRequiredView
23
24 from myslice.settings               import logger, config
25
26 from repoze.lru                     import lru_cache
27 from rest.json_encoder              import MyEncoder
28
29 def dispatch(request, method):
30
31     hrn = None
32     urn = None
33     object_type = None
34     rspec = None
35     output_format = None
36     recursive = False
37     # Have to be hashable for lru_cache
38     options   = frozenset() # dict()
39     platforms = frozenset() # list()
40
41     results = dict()
42     display = None
43
44     if request.method == 'POST':
45         req_items = request.POST
46     elif request.method == 'GET':
47         req_items = request.GET
48
49     logger.debug("URL = %s" % request.build_absolute_uri())
50
51     for el in req_items.items():
52         if el[0].startswith('rspec'):
53             rspec += el[1]
54         elif el[0].startswith('platform'):
55             platforms = frozenset(req_items.getlist('platform[]'))
56         elif el[0].startswith('options'):
57             options_url = el[1] #req_items.getlist('options')
58             options = QueryDict(options_url)
59         elif el[0].startswith('output_format'):
60             output_format = el[1] #req_items.getlist('options')
61         elif el[0].startswith('hrn'):
62             hrn = el[1]
63         elif el[0].startswith('urn'):
64             urn = el[1]
65         elif el[0].startswith('type'):
66             object_type = el[1]
67         elif el[0].startswith('recursive'):
68             if el[1] == '1':
69                 recursive = True
70             else:
71                 recursive = False
72         elif el[0].startswith('display'):
73             display = el[1]
74
75     start_time = time.time()
76     results = sfa_client(request, method, hrn, urn, object_type, rspec, recursive, options, platforms, output_format, False)
77     logger.debug("EXEC TIME - sfa_client() - %s sec." % (time.time() - start_time))
78     if display == 'table':
79         return render_to_response('table-default.html', {'data' : data, 'fields' : columns, 'id' : '@component_id', 'options' : None})
80     else:
81         return HttpResponse(json.dumps(results, cls=MyEncoder), content_type="application/json")
82
83 def get_user_account(request, user_email, platform_name):
84     """
85     Returns the user configuration for a given platform.
86     This function does not resolve references.
87     """
88     user_query  = Query().get('local:user').filter_by('email', '==', user_email).select('user_id')
89     user_details = execute_admin_query(request, user_query)
90     platform_query  = Query().get('local:platform').filter_by('platform', '==', platform_name).select('platform_id')
91     platform_details = execute_admin_query(request, platform_query)
92
93     account_query  = Query().get('local:account').filter_by('platform_id','==',platform_details[0]['platform_id']).filter_by('user_id', '==', user_details[0]['user_id']).select('user_id','platform_id','auth_type','config')
94     accounts = execute_admin_query(request, account_query)
95
96     if not accounts:
97         raise Exception, "this account does not exist"
98
99     if accounts[0]['auth_type'] == 'reference':
100         pf = json.loads(accounts[0]['config'])['reference_platform']
101         return get_user_account(request, user_email, pf)
102
103     return accounts[0]
104
105 #@lru_cache(100)
106 def sfa_client(request, method, hrn=None, urn=None, object_type=None, rspec=None, recursive=False, options=None, platforms=None, output_format=None, admin=False):
107
108     Config = ConfigParser.ConfigParser()
109     monitor_file = os.path.abspath(os.path.dirname(__file__) + '/../myslice/monitor.ini')
110     Config.read(monitor_file)
111
112     if admin:
113         user_email, admin_password = config.manifold_admin_user_password()
114     else:
115         #logger.debug(request.session['user']['email'])
116         user_email = request.session['user']['email']
117
118     results = dict()
119
120     if hrn is None:
121         hrn = ''
122     if urn is None:
123         urn = ''
124     if object_type is None:
125         object_type = ''
126     if rspec is None:
127         rspec = ''
128     if recursive is None:
129         recursive = False
130     if options is None:
131         options  = dict()
132     if platforms is None:
133         platforms = list()
134
135     if method not in ['GetVersion','ListResources']:
136         try:
137             if not hrn:
138                 hrn = urn_to_hrn(urn)
139             else:
140                 urn = hrn_to_urn(hrn, object_type) 
141         except Exception,e:
142             logger.error(e)
143             raise Exception, "Provide urn OR hrn + type as parameters of method %s" % method
144
145     if len(platforms)==0:
146         platforms = get_platforms(request)
147         #platforms.append('myslice')
148     #results = {'method':method,'platforms':platforms,'rspec':rspec,'options':options}
149
150     result = []
151     dict_result = {}
152     data = []
153     columns = []
154     api_options = {}
155     api_options['list_leases'] = 'all'
156     server_am = False
157     for pf in platforms:
158         platform = get_platform_config(request, pf)
159         logger.debug("platform={}".format(platform))
160         if 'sm' in platform and len(platform['sm']) > 0:
161             logger.debug('sm')
162             server_am = True
163             server_url = platform['sm']
164         if 'rm' in platform and len(platform['rm']) > 0:
165             logger.debug('rm')
166             server_am = False
167             server_url = platform['rm']
168         if 'registry' in platform and len(platform['registry']) > 0:
169             logger.debug('registry')
170             server_am = False
171             server_url = platform['registry']
172     
173         if not Config.has_option('monitor', 'cert') :
174              #return HttpResponse(json.dumps({'error' : '-1'}), content_type="application/json")
175              return {'error' : '-1', 'msg': 'monitor.ini has no cert configured'}
176
177         cert = os.path.abspath(Config.get('monitor', 'cert'))
178         if not os.path.isfile(cert) :
179              #return HttpResponse(json.dumps({'error' : '-1'}), content_type="application/json")
180              return {'error' : '-1', 'msg': 'check cert file at %s'%cert}
181
182         if not Config.has_option('monitor', 'pkey') :
183              #return HttpResponse(json.dumps({'error' : '-2'}), content_type="application/json")
184              return {'error' : '-2'}
185
186         pkey = os.path.abspath(Config.get('monitor', 'pkey'))
187         if not os.path.isfile(pkey) :
188              #return HttpResponse(json.dumps({'error' : '-2'}), content_type="application/json")
189              return {'error' : '-2'}
190  
191         server = SfaServerProxy(server_url, pkey, cert)
192         #server = SFAProxy(server_url, pkey, cert)
193         if 'geni_rspec_version' in options:
194             # GetVersion to know if the AM supports the requested version
195             # if not ask for the default GENI v3
196             start_time = time.time()
197             result = server.GetVersion()
198             logger.debug("EXEC TIME - GetVersion() - %s sec." % (time.time() - start_time))
199             logger.debug(result)
200             logger.debug(result['value'])
201             if 'geni_ad_rspec_versions' in result['value']:
202                 for v in result['value']['geni_ad_rspec_versions']:
203                     if v['type'] == options['geni_rspec_version']:
204                         api_options['geni_rspec_version'] = {'type': options['geni_rspec_version']}
205                         break
206                     else:
207                         api_options['geni_rspec_version'] = {'type': 'GENI', 'version': '3'}
208         else:
209             api_options['geni_rspec_version'] = {'type': 'GENI', 'version': '3'}
210
211         try:
212             # Get user config from Manifold
213             user_config = get_user_config(request, user_email, pf)
214             if 'delegated_user_credential' in user_config:
215                 logger.debug('delegated_user_credential')
216                 user_cred = user_config['delegated_user_credential']
217             elif 'user_credential' in user_config:
218                 logger.debug('user_credential')
219                 user_cred = user_config['user_credential']
220             else:
221                 logger.error("no user credentials for user = ", user_email)
222                 user_cred = {}
223
224             if object_type:
225                 if 'delegated_%s_credentials'%object_type in user_config:
226                     logger.debug('delegated_%s_credentials'%object_type)
227                     for obj_name, cred in user_config['delegated_%s_credentials'%object_type].items():
228                         if obj_name == hrn:
229                             object_cred = cred
230                 elif '%s_credentials'%object_type in user_config:
231                     logger.debug('%s_credentials'%object_type)
232                     for obj_name, cred in user_config['%s_credentials'%object_type].items():
233                         if obj_name == hrn:
234                             object_cred = cred
235                 else:
236                     logger.error("no credentials for object")
237                     logger.error(object_type)
238                     logger.error(object_name)
239                     object_cred = {}
240
241             # Both AM & Registry
242             if method == "GetVersion": 
243                 start_time = time.time()
244                 result = server.GetVersion()
245                 logger.debug("EXEC TIME - GetVersion() - %s sec." % (time.time() - start_time))
246             else:
247                 # AM API Calls
248                 if server_am:
249                     if method == "ListResources":
250                         logger.debug(api_options)
251                         #logger.debug(user_cred)
252                         start_time = time.time()
253                         result = server.ListResources([user_cred], api_options)
254                         logger.debug("EXEC TIME - ListResources() - %s sec." % (time.time() - start_time))
255                         #logger.debug(result)
256                         dict_result = xmltodict.parse(result['value'])
257                         result['parsed'] = dict_result
258                         if isinstance(dict_result['rspec']['node'], list):
259                             columns.extend(dict_result['rspec']['node'][0].keys())
260                         else:
261                             columns.extend(dict_result['rspec']['node'].keys())
262
263                     elif method == "Describe":
264                         start_time = time.time()
265                         version = server.GetVersion()
266                         logger.debug("EXEC TIME - GetVersion() - %s sec." % (time.time() - start_time))
267                         logger.debug(version['geni_api'])
268                         # if GetVersion = v2
269                         if version['geni_api'] == 2:
270                             # ListResources(slice_hrn)
271                             api_options['geni_slice_urn'] = urn
272                             result = server.ListResources([object_cred], api_options)
273                             dict_result = xmltodict.parse(result['value'])
274                         # else GetVersion = v3
275                         else:
276                             result = server.Describe([urn] ,[object_cred], api_options)
277                             if isinstance(result, dict):
278                                 if result['value'] != 0:
279                                     dict_result = xmltodict.parse(result['value']['geni_rspec'])
280
281                         result['parsed'] = dict_result
282                         if 'rspec' in dict_result and 'node' in dict_result['rspec']:
283                             if isinstance(dict_result['rspec']['node'], list):
284                                 columns.extend(dict_result['rspec']['node'][0].keys())
285                             else:
286                                 columns.extend(dict_result['rspec']['node'].keys())
287
288                     elif method == 'Renew':
289                         # Renew till 1 month from now
290                         d = datetime.datetime.utcnow() + datetime.timedelta(365/12)
291                         date = d.isoformat("T") + "Z"
292                         result = server.Renew([urn] ,[object_cred], date, api_options)
293                     elif method == 'Delete':
294                         result = server.Delete([urn] ,[object_cred], api_options)
295                     elif method == 'Allocate':
296                         api_options['call_id']    = unique_call_id()
297                         # List of users comes from the Registry
298                         api_options['sfa_users']  = sfa_users
299                         api_options['geni_users'] = geni_users
300                         # if GetVersion = v2
301                         version = server.GetVersion()
302                         if version['geni_api'] == 2:
303                             result = server.CreateSliver([urn] ,[object_cred], rspec, api_options)
304                         # else GetVersion = v3
305                         else:
306                             result = server.Allocate([urn] ,[object_cred], rspec, api_options)
307                     elif method == 'Provision':
308                         # if GetVersion = v2
309                         # Nothing it is not supported by v2 AMs
310                         version = server.GetVersion()
311                         if version['geni_api'] == 3:
312                             api_options['call_id']    = unique_call_id()
313                             # List of users comes from the Registry
314                             api_options['sfa_users']  = sfa_users
315                             api_options['geni_users'] = geni_users
316                             result = server.Provision([urn] ,[object_cred], api_options)
317                     elif method == 'Status':
318                         result = server.Status([urn] ,[object_cred], api_options)
319                     elif method == 'PerformOperationalAction':
320                         # if GetVersion = v2
321                         # Nothing it is not supported by v2 AMs
322                         version = server.GetVersion()
323                         if version['geni_api'] == 3:
324                             result = server.PerformOperationalAction([urn] ,[object_cred], action, api_options)
325                     elif method == 'Shutdown':
326                         result = server.Shutdown(urn ,[object_cred], api_options)
327                     else:
328                         #return HttpResponse(json.dumps({'error' : '-3','msg':'method not supported by AM'}), content_type="application/json")
329                         logger.debug('method %s not handled by AM' % method)
330                         result = []
331
332                 # Registry API Calls 
333                 else:
334                     record_dict = {'urn': urn, 'hrn': hrn, 'type': object_type}
335                     if method == "List":
336                         # hrn is required
337                         api_options['recursive'] = recursive
338                         result = server.List(hrn, user_cred, api_options)
339                         if object_type:
340                             result = filter_records(object_type, result)
341                     elif method == "Resolve":
342                         # hrn is required
343                         # details can be True or False
344                         api_options['details']=True
345                         result = server.Resolve(hrn, user_cred, api_options)
346                         if object_type:
347                             result = filter_records(object_type, result)
348                     elif method == "Register":
349                         # record_dict must be crafted
350                         # auth_cred must be selected in the list of auth_creds from user's account
351                         result = server.Register(record_dict, auth_cred)
352                     elif method == "Update":
353                         # record_dict must be crafted
354                         # object_cred must be selected in the list of creds for the object type
355                         # from user's account
356                         result = server.Update(record_dict, object_cred)
357                     elif method == "Remove":
358                         # hrn is required
359                         # auth_cred must be selected in the list of auth_creds from user's account
360                         # object_type is required
361                         result = server.Remove(hrn, auth_cred, object_type)
362                     else:
363                         #return HttpResponse(json.dumps({'error' : '-3','msg':'method not supported by Registry'}), content_type="application/json")
364                         logger.debug('method %s not handled by Registry' % method)
365                         result = []
366             if output_format is not None:
367                 if 'value' in result:
368                     # TODO Python Caching 
369                     # to avoid translating the same RSpec in the same format several times
370                     start_time = time.time()
371                     result = translate(result['value'],output_format)
372                     logger.debug("EXEC TIME - translate() - %s sec." % (time.time() - start_time))
373
374             results[pf] = result
375             if dict_result:
376                 if 'rspec' in dict_result and 'node' in dict_result['rspec']:
377                     if isinstance(dict_result['rspec']['node'], list):
378                         data = data + dict_result['rspec']['node']
379                     else:
380                         data.append(dict_result['rspec']['node'])
381         except Exception,e:
382             import traceback
383             logger.error(traceback.format_exc())
384             logger.error(e)
385             results[pf] = {'error':'-3', 'result':result,'error_msg': str(e)}
386
387     results['columns'] = columns
388     return results
389
390 @lru_cache(100)
391 def translate(rspec, output_format):
392     import urllib
393     import urllib2
394
395     values = {'content' : rspec}
396     url = 'https://demo.fiteagle.org/omnweb/convert/to/' + output_format
397     data = urllib.urlencode(values)
398     req = urllib2.Request(url, data)
399     response = urllib2.urlopen(req)
400     return response.read()
401
402 def get_user_config(request, user_email, platform_name):
403     account = get_user_account(request, user_email, platform_name)
404     return json.loads(account['config']) if account['config'] else {}
405
406 def get_platforms(request):
407     ret = list()
408     platform_query  = Query().get('local:platform').filter_by('gateway_type', '==', 'sfa').filter_by('disabled','==',0).select('platform')
409     platforms = execute_admin_query(request, platform_query)
410
411     for p in platforms:
412         ret.append(p['platform'])
413     return ret
414
415 def get_platform_config(request, platform_name):
416     platform_query  = Query().get('local:platform').filter_by('platform', '==', platform_name).select('platform', 'config')
417     platforms = execute_admin_query(request, platform_query)
418
419     return json.loads(platforms[0]['config']) if platforms[0]['config'] else {}
420
421 def filter_records(type, records):
422     filtered_records = []
423     for record in records:
424         if (record['type'] == type) or (type == "all"):
425             filtered_records.append(record)
426     return filtered_records