blob: 3cf400b7739c9c1c2d60d3b02d26011eff8959df [file] [log] [blame]
mblighe8819cd2008-02-15 16:48:40 +00001"""\
2RPC request handler Django. Exposed RPC interface functions should be
3defined in rpc_interface.py.
4"""
5
6__author__ = 'showard@google.com (Steve Howard)'
7
showard02ed4bd2009-09-09 15:30:11 +00008import traceback, pydoc, re, urllib, logging, logging.handlers
showarda5288b42009-07-28 20:06:08 +00009from autotest_lib.frontend.afe.json_rpc import serviceHandler
showard64a95952010-01-13 21:27:16 +000010from autotest_lib.frontend.afe import models, rpc_utils
showard02ed4bd2009-09-09 15:30:11 +000011from autotest_lib.client.common_lib import global_config
12from autotest_lib.frontend.afe import rpcserver_logging
13
showard02ed4bd2009-09-09 15:30:11 +000014LOGGING_REGEXPS = [r'.*add_.*',
15 r'delete_.*',
16 r'.*_remove_.*',
17 r'modify_.*',
18 r'create.*']
19FULL_REGEXP = '(' + '|'.join(LOGGING_REGEXPS) + ')'
20COMPILED_REGEXP = re.compile(FULL_REGEXP)
21
22
23def should_log_message(name):
24 return COMPILED_REGEXP.match(name)
mblighe8819cd2008-02-15 16:48:40 +000025
mblighe8819cd2008-02-15 16:48:40 +000026
27class RpcMethodHolder(object):
jadmanski0afbb632008-06-06 21:10:57 +000028 'Dummy class to hold RPC interface methods as attributes.'
mblighe8819cd2008-02-15 16:48:40 +000029
30
showard7c785282008-05-29 19:45:12 +000031class RpcHandler(object):
jadmanski0afbb632008-06-06 21:10:57 +000032 def __init__(self, rpc_interface_modules, document_module=None):
33 self._rpc_methods = RpcMethodHolder()
showardef6fe022009-03-27 20:55:16 +000034 self._dispatcher = serviceHandler.ServiceHandler(self._rpc_methods)
mblighe8819cd2008-02-15 16:48:40 +000035
jadmanski0afbb632008-06-06 21:10:57 +000036 # store all methods from interface modules
37 for module in rpc_interface_modules:
38 self._grab_methods_from(module)
showard7c785282008-05-29 19:45:12 +000039
jadmanski0afbb632008-06-06 21:10:57 +000040 # get documentation for rpc_interface we can send back to the
41 # user
42 if document_module is None:
43 document_module = rpc_interface_modules[0]
44 self.html_doc = pydoc.html.document(document_module)
showard7c785282008-05-29 19:45:12 +000045
46
showardef6fe022009-03-27 20:55:16 +000047 def get_rpc_documentation(self):
showard2d7ac832009-06-22 18:14:10 +000048 return rpc_utils.raw_http_response(self.html_doc)
showardef6fe022009-03-27 20:55:16 +000049
50
showard3d6ae112009-05-02 00:45:48 +000051 def raw_request_data(self, request):
showardef6fe022009-03-27 20:55:16 +000052 if request.method == 'POST':
53 return request.raw_post_data
54 return urllib.unquote(request.META['QUERY_STRING'])
55
56
showard3d6ae112009-05-02 00:45:48 +000057 def execute_request(self, json_request):
58 return self._dispatcher.handleRequest(json_request)
59
60
61 def decode_request(self, json_request):
62 return self._dispatcher.translateRequest(json_request)
63
64
65 def dispatch_request(self, decoded_request):
66 return self._dispatcher.dispatchRequest(decoded_request)
67
68
showard02ed4bd2009-09-09 15:30:11 +000069 def log_request(self, user, decoded_request, decoded_result,
70 log_all=False):
71 if log_all or should_log_message(decoded_request['method']):
72 msg = '%s:%s %s' % (decoded_request['method'], user,
73 decoded_request['params'])
74 if decoded_result['err']:
75 msg += '\n' + decoded_result['err_traceback']
76 rpcserver_logging.rpc_logger.error(msg)
77 else:
78 rpcserver_logging.rpc_logger.info(msg)
79
80
81 def encode_result(self, results):
82 return self._dispatcher.translateResult(results)
83
84
showardef6fe022009-03-27 20:55:16 +000085 def handle_rpc_request(self, request):
showard64a95952010-01-13 21:27:16 +000086 user = models.User.current_user()
showard3d6ae112009-05-02 00:45:48 +000087 json_request = self.raw_request_data(request)
showard02ed4bd2009-09-09 15:30:11 +000088 decoded_request = self.decode_request(json_request)
89 decoded_result = self.dispatch_request(decoded_request)
90 result = self.encode_result(decoded_result)
showard902c96c2009-09-11 18:47:35 +000091 if rpcserver_logging.LOGGING_ENABLED:
showard02ed4bd2009-09-09 15:30:11 +000092 self.log_request(user, decoded_request, decoded_result)
showard3d6ae112009-05-02 00:45:48 +000093 return rpc_utils.raw_http_response(result)
showardef6fe022009-03-27 20:55:16 +000094
95
96 def handle_jsonp_rpc_request(self, request):
97 request_data = request.GET['request']
98 callback_name = request.GET['callback']
99 # callback_name must be a simple identifier
100 assert re.search(r'^\w+$', callback_name)
101
showard3d6ae112009-05-02 00:45:48 +0000102 result = self.execute_request(request_data)
showardef6fe022009-03-27 20:55:16 +0000103 padded_result = '%s(%s)' % (callback_name, result)
showard3d6ae112009-05-02 00:45:48 +0000104 return rpc_utils.raw_http_response(padded_result,
105 content_type='text/javascript')
showardef6fe022009-03-27 20:55:16 +0000106
107
jadmanski0afbb632008-06-06 21:10:57 +0000108 @staticmethod
109 def _allow_keyword_args(f):
110 """\
111 Decorator to allow a function to take keyword args even though
112 the RPC layer doesn't support that. The decorated function
113 assumes its last argument is a dictionary of keyword args and
114 passes them to the original function as keyword args.
115 """
116 def new_fn(*args):
117 assert args
118 keyword_args = args[-1]
119 args = args[:-1]
120 return f(*args, **keyword_args)
121 new_fn.func_name = f.func_name
122 return new_fn
showard7c785282008-05-29 19:45:12 +0000123
124
jadmanski0afbb632008-06-06 21:10:57 +0000125 def _grab_methods_from(self, module):
126 for name in dir(module):
showard5deef7f2009-09-09 18:16:58 +0000127 if name.startswith('_'):
128 continue
jadmanski0afbb632008-06-06 21:10:57 +0000129 attribute = getattr(module, name)
130 if not callable(attribute):
131 continue
showardef6fe022009-03-27 20:55:16 +0000132 decorated_function = RpcHandler._allow_keyword_args(attribute)
jadmanski0afbb632008-06-06 21:10:57 +0000133 setattr(self._rpc_methods, name, decorated_function)