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