cc1  v2.1
CC1 source code docs
 All Classes Namespaces Files Functions Variables Pages
decorators.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 # @COPYRIGHT_begin
3 #
4 # Copyright [2010-2014] Institute of Nuclear Physics PAN, Krakow, Poland
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18 # @COPYRIGHT_end
19 
20 ##
21 # @package src.cm.utils.decorators
22 # Here are placed decorators for:
23 # - \b actor functions
24 # (and the src.cm.utils.decorators.genericlog() function called by all those),
25 # - \b ctx functions,
26 # - \b rm functions.
27 #
28 # @par Actor decorators
29 # - src.cm.utils.decorators.guest_log
30 # - src.cm.utils.decorators.user_log
31 # - src.cm.utils.decorators.admin_cm_log
32 #
33 # Those actor decorators call src.cm.utils.decorators.genericlog().
34 # By default those decorators call src.cm.utils.decorators.genericlog
35 # with logging disabled. You can enable it by giving kwarg \c log=True ,
36 # when decorating, eg.:
37 #
38 # @code
39 # @admin_clm_log(log=False)
40 # def get_by_id(cm_id, caller_id, id):
41 # pass
42 # @endcode
43 #
44 # @author Tomasz Sośnicki <tom.sosnicki@gmail.com>
45 # @author Maciej Nabożny <mn@mnabozny.pl>
46 #
47 from cm.utils import log
48 from cm.utils.exception import CMException
49 from cm.utils.message import MESSAGES
50 from common import response # HandleQuerySets
51 from cm.models.admin import Admin
52 from functools import wraps
53 import json
54 from django.http import HttpResponse
55 from django.db import transaction
56 from threading import Lock
57 
58 # # Set of functions decorated by actor decorators
59 # (cm.utils.decorators.guest_log(), src.cm.utils.decorators.user_log(),
60 # src.cm.utils.decorators.admin_cm_log())
61 from common.utils import json_convert
62 
63 global decorated_functions
64 global ci_decorated_functions
65 global ctx_decorated_functions
66 
67 decorated_functions = set([])
68 ci_decorated_functions = set([])
69 ctx_decorated_functions = set([])
70 
71 
72 locks = {
73  'vmcreate': Lock()
74 }
75 
76 # Every actor decorators add the decorated function to global decorated_functions and send it
77 # to genericlog but with different arguments
78 
79 
80 ##
81 #
82 # Decorator for functions requiring only \b guest's privilidges.
83 #
84 # src.cm.utils.decorators.genericlog() is called with parameters:
85 # - \c is_user=False
86 # - \c is_superuser=False
87 #
88 # @par Decorated function's declaration
89 # @code
90 # @guest_log[(log=<False|True>)]
91 # function (*arg, **kw)
92 # @endcode
93 #
94 # @par Decorated function's call
95 # @code
96 # function (*arg, **kw)
97 # @endcode
98 #
99 def guest_log(*arg, **kw):
100  def logwrapper(fun):
101  @wraps(fun)
102  def wrapper(*args, **kwargs):
103  return genericlog(log_enabled=kw.get('log', False), is_user=False, is_admin_cm=False, need_ip=False, fun=fun, args=args)
104 
105  decorated_functions.add(wrapper)
106 
107  return wrapper
108  return logwrapper
109 
110 
111 ##
112 #
113 # Decorator for functions requiring logged in \b user's privilidges.
114 #
115 # src.cm.utils.decorators.genericlog() is called with parameters:
116 # - \c is_user=True
117 # - \c is_superuser=False
118 #
119 # @par Decorated function's declaration
120 # @code
121 # @user_log[(log=<False|True>)]
122 # function (cm_id, caller_id, *args, **kw)
123 # @endcode
124 #
125 # @par Decorated function's call
126 # @code
127 # function (caller_id, *arg, **kw)
128 # @endcode
129 #
130 def user_log(*arg, **kw):
131  def logwrapper(fun):
132  @wraps(fun)
133  def wrapper(*args, **kwargs):
134  return genericlog(log_enabled=kw.get('log', False), is_user=True, is_admin_cm=False, need_ip=False, fun=fun, args=args)
135 
136  decorated_functions.add(wrapper)
137 
138  return wrapper
139  return logwrapper
140 
141 
142 ##
143 #
144 # Decorator for functions requiring \b admin_cm's privilidges.
145 #
146 # src.cm.utils.decorators.genericlog is called with parameters:
147 # - \c is_user=True
148 # - \c is_superuser=True
149 #
150 # @par Decorated function's declaration
151 # @code
152 # @admin_clm_log[(log=<False|True>)]
153 # function (cm_id, caller_id, *args, **kw)
154 # @endcode
155 #
156 # @par Decorated function's call
157 # @code
158 # function (caller_id, admin_password, *arg, **kw)
159 # @endcode
160 #
161 # \c admin_password argument is removed by
162 # \c src.cm.utils.decorators.genericlog(), so it doesn't appear in formal
163 # parameters of the function.
164 #
165 def admin_cm_log(*arg, **kw):
166  def logwrapper(fun):
167  @wraps(fun)
168  def wrapper(*args, **kwargs):
169  return genericlog(log_enabled=kw.get('log', False), is_user=True, is_admin_cm=True, need_ip=False, fun=fun, args=args)
170 
171  decorated_functions.add(wrapper)
172 
173  return wrapper
174  return logwrapper
175 
176 
177 ##
178 #
179 # Decorator for functions requiring only \b guest's privilidges.
180 #
181 # src.cm.utils.decorators.genericlog() is called with parameters:
182 # - \c is_user=False
183 # - \c is_superuser=False
184 #
185 # @par Decorated function's declaration
186 # @code
187 # @guest_log[(log=<False|True>)]
188 # function (*arg, **kw)
189 # @endcode
190 #
191 # @par Decorated function's call
192 # @code
193 # function (*arg, **kw)
194 # @endcode
195 #
196 def ci_log(*arg, **kw):
197  def logwrapper(fun):
198  @wraps(fun)
199  def wrapper(*args, **kwargs):
200  return genericlog(log_enabled=kw.get('log', False), is_user=False, is_admin_cm=False, need_ip=True, fun=fun, args=args)
201 
202  ci_decorated_functions.add(wrapper)
203 
204  return wrapper
205  return logwrapper
206 
207 
208 ##
209 #
210 # Decorator for functions requiring only \b guest's privilidges.
211 #
212 # src.cm.utils.decorators.genericlog() is called with parameters:
213 # - \c is_user=False
214 # - \c is_superuser=False
215 #
216 # @par Decorated function's declaration
217 # @code
218 # @guest_log[(log=<False|True>)]
219 # function (*arg, **kw)
220 # @endcode
221 #
222 # @par Decorated function's call
223 # @code
224 # function (*arg, **kw)
225 # @endcode
226 #
227 def ctx_log(*arg, **kw):
228  def logwrapper(fun):
229  @wraps(fun)
230  def wrapper(request, *args, **kwargs):
231  data = request.GET.dict()
232  data['remote_ip'] = request.META.get('REMOTE_ADDR')
233  # log.debug(0, 'RAW ARGS: %s' % str(data))
234 
235  gen_exception = False
236  log_enabled = kw.get('log', False)
237  name = '%s.%s' % (fun.__module__.replace('cm.views.', ''), fun.__name__)
238  if log_enabled:
239  log.debug(0, '=' * 100)
240  log.debug(0, 'Function: %s' % name)
241  log.debug(0, 'Args:\n%s' % json.dumps(data, indent=4))
242  with transaction.commit_manually():
243  try:
244  # Execute function
245  resp = fun(**data)
246  transaction.commit()
247  except CMException, e:
248  transaction.rollback()
249  log.exception(0, 'CMException %s' % e)
250  resp = e.response
251  except Exception, e:
252  transaction.rollback()
253  gen_exception = True
254  resp = response('cm_error', str(e))
255 
256  if resp['status'] != 'ok' and not log_enabled:
257  log.debug(0, '=' * 100)
258  log.debug(0, 'Function: %s' % name)
259  log.debug(0, 'ARGS: %s' % str(data))
260  if resp['status'] != 'ok' or log_enabled:
261  if gen_exception:
262  log.exception(0, 'General exception')
263  log.debug(0, 'Response: %s' % resp or 'None')
264 
265  return HttpResponse(json.dumps(resp, default=json_convert))
266 
267  ctx_decorated_functions.add(wrapper)
268 
269  return wrapper
270  return logwrapper
271 
272 
273 ##
274 #
275 # Decorator for functions requiring only \b guest's privilidges.
276 #
277 # src.cm.utils.decorators.genericlog() is called with parameters:
278 # - \c is_user=False
279 # - \c is_superuser=False
280 #
281 # @par Decorated function's declaration
282 # @code
283 # @guest_log[(log=<False|True>)]
284 # function (*arg, **kw)
285 # @endcode
286 #
287 # @par Decorated function's call
288 # @code
289 # function (*arg, **kw)
290 # @endcode
291 #
292 def ec2ctx_log(*arg, **kw):
293  def logwrapper(fun):
294  @wraps(fun)
295  def wrapper(request, *args, **kwargs):
296  log.debug(0, "request\n%s: " % json.dumps(request.GET.dict(), indent=4))
297  log_enabled = kw.get('log', False)
298  name = '%s.%s' % (fun.__module__.replace('cm.views.', ''), fun.__name__)
299  if log_enabled:
300  log.debug(0, '=' * 100)
301  log.debug(0, 'Function: %s' % name)
302 
303  resp = None
304  try:
305  resp = fun(request, *args, **kwargs)
306  except CMException, e:
307  log.exception(0, 'CMException %s' % e)
308  except Exception, e:
309  log.exception(0, 'Exception %s' % e)
310 
311  return HttpResponse(resp)
312  return wrapper
313  return logwrapper
314 
315 
316 ##
317 #
318 # Generic log is called by actor decorators defined in src.clm.utils.decorators :
319 # - src.cm.utils.decorators.guest_log
320 # - src.cm.utils.decorators.user_log
321 # - src.cm.utils.decorators.admin_cm_log
322 #
323 # It calls decorated functions, additionally performing several tasks.
324 #
325 # Genericlog performes:
326 #
327 # -# <i>if decorated function requires user or admin privilidges</i>: <b>authorization</b>;
328 # -# <b>execution</b> of the decorated function;
329 # -# <i>if \c log_enabled=TRUE or if return status isn't 'ok'</i>: <b>debug log</b> of the \c user_id, function name and arguments;
330 # -# <i>if exception is thrown</i>: <b>general exception log</b>;
331 # -# <i>if return status isn't 'ok' or \c log_enabled:</i> <b>debug log</b> of the response.
332 #
333 # @returns{dict} HttpResponse response with content of JSON-ed tuple
334 # (status, data), where status should be "ok" if everything went fine.
335 #
336 def genericlog(log_enabled, is_user, is_admin_cm, need_ip, fun, args):
337  #===========================================================================
338  # AUTORIZATION
339  #===========================================================================
340  name = '%s.%s' % (fun.__module__.replace('cm.views.', ''), fun.__name__)
341 
342  request = args[0]
343  # log.debug(0, 'BODY: %s' % request.body)
344  data = json.loads(request.body)
345 
346  lock_name = None
347  print data
348 
349  if is_user:
350  if len(args) < 1:
351  return response('cm_error', "missing arguments")
352 
353  caller_id = data['caller_id']
354 
355  if name in ('user.vm.create', 'user.farm.create', 'admin_cm.vm.create', 'admin_cm.farm.create'):
356  lock_name = 'vmcreate'
357  log.debug(caller_id, 'Try acquire lock vmcreate')
358  locks[lock_name].acquire()
359  log.debug(caller_id, 'Lock vmcreate acquired')
360 
361  if is_admin_cm:
362  cm_password = data.pop('cm_password')
363  try:
364  Admin.check_password(caller_id, cm_password)
365  except Exception:
366  return HttpResponse(json.dumps(response('user_permission'), default=json_convert))
367  else:
368  caller_id = 0
369 
370  if need_ip:
371  data['remote_ip'] = request.META.get('REMOTE_ADDR')
372 
373  #===========================================================================
374  # LOG AGRUMENTS
375  #===========================================================================
376  gen_exception = False
377  if log_enabled:
378  log.debug(caller_id, '=' * 100)
379  log.debug(caller_id, 'Function: %s' % name)
380  log.debug(caller_id, 'Args:\n%s' % json.dumps(data, indent=4))
381 
382  with transaction.commit_manually():
383  try:
384  # Execute function
385  resp = response('ok', fun(**data))
386  transaction.commit()
387  except CMException, e:
388  transaction.rollback()
389  log.exception(caller_id, 'CMException %s' % e)
390  resp = e.response
391  except Exception, e:
392  transaction.rollback()
393  gen_exception = True
394  resp = response('cm_error', str(e))
395  finally:
396  if lock_name:
397  log.debug(caller_id, 'Try release lock vmcreate')
398  locks[lock_name].release()
399  log.debug(caller_id, 'Lock vmcreate released')
400  # adding messages to response
401  global MESSAGES
402  if MESSAGES:
403  resp['messages'] = MESSAGES.copy()
404  MESSAGES.clear()
405  if resp['status'] != 'ok' and not log_enabled:
406  log.debug(caller_id, '=' * 100)
407  log.debug(caller_id, 'Function: %s' % name)
408  log.debug(caller_id, 'ARGS: %s' % str(data))
409  if resp['status'] != 'ok' or log_enabled:
410  if gen_exception:
411  log.exception(caller_id, 'General exception')
412  log.debug(caller_id, 'Response: %s' % resp or 'None')
413  return HttpResponse(json.dumps(resp, default=json_convert))
414