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