serviceHandler.py revision 0f5f0443e9286483365d6978ff432d8e4029d599
12be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 22be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi""" 32be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi Copyright (c) 2007 Jan-Klaas Kollhof 42be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 52be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi This file is part of jsonrpc. 62be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 74ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn jsonrpc is free software; you can redistribute it and/or modify 84ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn it under the terms of the GNU Lesser General Public License as published by 94ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn the Free Software Foundation; either version 2.1 of the License, or 104ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn (at your option) any later version. 114ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn 124ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn This software is distributed in the hope that it will be useful, 132be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi but WITHOUT ANY WARRANTY; without even the implied warranty of 142be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 152be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi GNU Lesser General Public License for more details. 16e7eb2bf306af6e8408cd77125861542d19e5ec6dUtkarsh Sanghi 172be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi You should have received a copy of the GNU Lesser General Public License 186ea135676cf391fac45b0051242ccac935c8bc62Utkarsh Sanghi along with this software; if not, write to the Free Software 192be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 202be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi""" 214ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn 222be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghiimport traceback 232be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 242be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghifrom json import decoder, encoder 252be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghifrom autotest_lib.site_utils.graphite import stats 262be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 272be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghidef customConvertJson(value): 282be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi """\ 292be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi Recursively process JSON values and do type conversions. 302be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi -change floats to ints 312be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi -change unicodes to strs 322be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi """ 332be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi if isinstance(value, float): 342be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi return int(value) 352be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi elif isinstance(value, unicode): 362be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi return str(value) 372be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi elif isinstance(value, list): 382be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi return [customConvertJson(item) for item in value] 392be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi elif isinstance(value, dict): 402be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi new_dict = {} 412be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi for key, val in value.iteritems(): 422be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi new_key = customConvertJson(key) 432be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi new_val = customConvertJson(val) 442be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi new_dict[new_key] = new_val 452be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi return new_dict 462be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi else: 472be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi return value 482be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 492be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghijson_encoder = encoder.JSONEncoder() 502be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghijson_decoder = decoder.JSONDecoder() 512be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 522be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 532be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghidef ServiceMethod(fn): 542be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi fn.IsServiceMethod = True 552be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi return fn 562be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 572be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghiclass ServiceException(Exception): 582be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi pass 592be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 602be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghiclass ServiceRequestNotTranslatable(ServiceException): 612be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi pass 622be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 632be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghiclass BadServiceRequest(ServiceException): 642be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi pass 652be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 662be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghiclass ServiceMethodNotFound(ServiceException): 672be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi pass 682be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 692be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 702be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghiclass ServiceHandler(object): 712be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 722be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi def __init__(self, service): 732be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi self.service=service 742be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 752be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 762be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi @classmethod 772be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi def blank_result_dict(cls): 782be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi return {'id': None, 'result': None, 'err': None, 'err_traceback': None} 792be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 802be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi def dispatchRequest(self, request): 816f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi """ 822be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi Invoke a json RPC call from a decoded json request. 83a5a2f2ea49e0085bf8d7f6f2b6e7cd624d710c01Utkarsh Sanghi @param request: a decoded json_request 84a5a2f2ea49e0085bf8d7f6f2b6e7cd624d710c01Utkarsh Sanghi @returns a dictionary with keys id, result, err and err_traceback 85a5a2f2ea49e0085bf8d7f6f2b6e7cd624d710c01Utkarsh Sanghi """ 860ebbc58fe6d45378a5b502c33eb1c4289fd8b05bUtkarsh Sanghi results = self.blank_result_dict() 872be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 882be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi try: 892be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi results['id'] = self._getRequestId(request) 902be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi methName = request['method'] 916f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi args = request['params'] 926f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi except KeyError: 936f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi raise BadServiceRequest(request) 946f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi 956f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi stats.Counter('rpc').increment(methName) 962be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi timer = stats.Timer('rpc') 974ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn 984ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn try: 994ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn meth = self.findServiceEndpoint(methName) 1004ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn timer.start() 1014ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn results['result'] = self.invokeServiceEndpoint(meth, args) 1024ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn except Exception, err: 1034ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn results['err_traceback'] = traceback.format_exc() 1044ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn results['err'] = err 1054ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn finally: 1064ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn timer.stop(methName) 1074ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn 1084ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn return results 1094ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn 1104ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn 1114ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn def _getRequestId(self, request): 1124ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn try: 1132be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi return request['id'] 1142be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi except KeyError: 1152be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi raise BadServiceRequest(request) 1162be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 1172be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 1182be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi def handleRequest(self, jsonRequest): 1192be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi request = self.translateRequest(jsonRequest) 1202be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi results = self.dispatchRequest(request) 1212be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi return self.translateResult(results) 1222be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 1232be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 1246f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi @staticmethod 1252be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi def translateRequest(data): 1266f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi try: 1276f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi req = json_decoder.decode(data) 1286ea135676cf391fac45b0051242ccac935c8bc62Utkarsh Sanghi except: 1290ebbc58fe6d45378a5b502c33eb1c4289fd8b05bUtkarsh Sanghi raise ServiceRequestNotTranslatable(data) 1302be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi req = customConvertJson(req) 1316f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi return req 1322be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 1332be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi def findServiceEndpoint(self, name): 1346f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi try: 1356f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi meth = getattr(self.service, name) 1366f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi return meth 1376f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi except AttributeError: 1386f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi raise ServiceMethodNotFound(name) 1392be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 1402be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi def invokeServiceEndpoint(self, meth, args): 1412be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi return meth(*args) 1422be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 1432be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi @staticmethod 1442be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi def translateResult(result_dict): 1452be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi """ 1462be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi @param result_dict: a dictionary containing the result, error, traceback 1472be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi and id. 1482be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi @returns translated json result 1492be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi """ 1502be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi if result_dict['err'] is not None: 1512be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi error_name = result_dict['err'].__class__.__name__ 1522be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi result_dict['err'] = {'name': error_name, 1534ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn 'message': str(result_dict['err']), 1544ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn 'traceback': result_dict['err_traceback']} 1554ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn result_dict['result'] = None 1562be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 1572be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi try: 1582be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi json_dict = {'result': result_dict['result'], 1592be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 'id': result_dict['id'], 1602be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 'error': result_dict['err'] } 1612be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi data = json_encoder.encode(json_dict) 1622be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi except TypeError, e: 1632be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi err_traceback = traceback.format_exc() 1642be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi print err_traceback 1652be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi err = {"name" : "JSONEncodeException", 1662be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi "message" : "Result Object Not Serializable", 1672be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi "traceback" : err_traceback} 1682be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi data = json_encoder.encode({"result":None, "id":result_dict['id'], 1692be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi "error":err}) 1702be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi 1712be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi return data 1722be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi