def dispatcher(self): # TODO: make decorator or something d = Dispatcher() d.add_method(self.request_player_state, name="request_state") d.add_method(self.player_change_state, name="state") d.add_method(self.register_player, name="register") d.add_method(self.register_controller, name="subscribe") d.add_method(self.player_command, name="command") return d
class GpioCoreServer: def __init__(self, rpc_socket, gpio_manager): self.rpc_socket = rpc_socket self.gpio_manager = gpio_manager self.dispatcher = Dispatcher() self.dispatcher.add_method(self.gpio_manager.add_input) self.dispatcher.add_method(self.gpio_manager.add_output) self.dispatcher.add_method(self.gpio_manager.pin_read) self.dispatcher.add_method(self.gpio_manager.pin_on) self.dispatcher.add_method(self.gpio_manager.pin_off) self.dispatcher.add_method(self.gpio_manager.enable_pub_when_activated) self.dispatcher.add_method(self.gpio_manager.disable_pub_when_activated) self.dispatcher.add_method(self.gpio_manager.enable_pub_when_deactivated) self.dispatcher.add_method(self.gpio_manager.disable_pub_when_deactivated) def run(self): try: while True: message = self.rpc_socket.recv_string() response = JSONRPCResponseManager.handle(message, self.dispatcher) self.rpc_socket.send_string(response.json) finally: self.gpio_manager.clean_up()
class TCSListener(resource.Resource): """ TCSListener Class is comprised of HTTP interface which listens for the end user requests, Worker Registry Handler, Work Order Handler and Work Order Receipts Handler . """ # The isLeaf instance variable describes whether or not a resource will have children and only leaf resources get rendered. # TCSListener is the most derived class hence isLeaf is required. isLeaf = True # ----------------------------------------------------------------- def __init__(self, config): try: (self.kv_helper, _) = connector.open(config) except Exception as err: logger.error(f"failed to open db: {err}") sys.exit(-1) # Worker registry handler needs to be instantiated before Work order handler. Otherwise, LMDB operations don't operate on updated values. # TODO: Needs further investigation on what is causing the above behavior. self.worker_registry_handler = TCSWorkerRegistryHandler(self.kv_helper) self.workorder_handler = TCSWorkOrderHandler( self.kv_helper, config["Listener"]["max_work_order_count"]) self.workorder_receipt_handler = TCSWorkOrderReceiptHandler( self.kv_helper) self.worker_encryption_key_handler = WorkerEncryptionKeyHandler( self.kv_helper) self.dispatcher = Dispatcher() rpc_methods = [ self.worker_encryption_key_handler.EncryptionKeyGet, self.worker_encryption_key_handler.EncryptionKeySet, self.worker_registry_handler.WorkerLookUp, self.worker_registry_handler.WorkerLookUpNext, self.worker_registry_handler.WorkerRegister, self.worker_registry_handler.WorkerSetStatus, self.worker_registry_handler.WorkerRetrieve, self.worker_registry_handler.WorkerUpdate, self.workorder_handler.WorkOrderSubmit, self.workorder_handler.WorkOrderGetResult, ] for m in rpc_methods: self.dispatcher.add_method(m) def _process_request(self, input_json_str): response = {} response['error'] = {} response['error'][ 'code'] = WorkOrderStatus.INVALID_PARAMETER_FORMAT_OR_VALUE try: input_json = json.loads(input_json_str) except: response['error'][ 'message'] = 'Error: Improper Json. Unable to load' return response if ('jsonrpc' not in input_json or 'id' not in input_json or 'method' not in input_json or 'params' not in input_json): response['error'][ 'message'] = 'Error: Json does not have the required field' return response if not isinstance(input_json['id'], int): response['error'][ 'message'] = 'Error: Id should be of type integer' return response response['jsonrpc'] = input_json['jsonrpc'] response['id'] = input_json['id'] if not isinstance(input_json['method'], str): response['error'][ 'message'] = 'Error: Method has to be of type string' return response if ("WorkOrderReceipt" in input_json['method']): return self.workorder_receipt_handler.workorder_receipt_handler( input_json_str) logger.info("Received request: %s", input_json['method']) # save the full json for WorkOrderSubmit input_json["params"]["raw"] = input_json_str data = json.dumps(input_json).encode('utf-8') response = JSONRPCResponseManager.handle(data, self.dispatcher) return response.data def render_GET(self, request): # JRPC response with id 0 is returned because id parameter # will not be found in GET request response = utility.create_error_response( WorkOrderStatus.INVALID_PARAMETER_FORMAT_OR_VALUE, "0", "Only POST request is supported") logger.error( "GET request is not supported. Only POST request is supported") return response def render_POST(self, request): response = {} logger.info('Received a new request from the client') try: # process the message encoding encoding = request.getHeader('Content-Type') data = request.content.read() if encoding == 'application/json': try: input_json_str = data.decode('utf-8') input_json = json.loads(input_json_str) jrpc_id = input_json["id"] response = self._process_request(input_json_str) except AttributeError: logger.error("Error while loading input json") response = utility.create_error_response( WorkOrderStatus.UNKNOWN_ERROR, jrpc_id, "UNKNOWN_ERROR: Error while loading the input JSON file" ) return response else: # JRPC response with 0 as id is returned because id can't be fecthed # from a request with unknown encoding response = utility.create_error_response( WorkOrderStatus.UNKNOWN_ERROR, 0, "UNKNOWN_ERROR: unknown message encoding") return response except: logger.exception('exception while decoding http request %s', request.path) # JRPC response with 0 as id is returned because id can't be # fetched from improper request response = utility.create_error_response( WorkOrderStatus.UNKNOWN_ERROR, 0, "UNKNOWN_ERROR: unable to decode incoming request") return response # send back the results try: if encoding == 'application/json': response = json.dumps(response) logger.info('response[%s]: %s', encoding, response) request.setHeader('content-type', encoding) request.setResponseCode(http.OK) return response.encode('utf8') except: logger.exception('unknown exception while processing request %s', request.path) response = utility.create_error_response( WorkOrderStatus.UNKNOWN_ERROR, jrpc_id, "UNKNOWN_ERROR: unknown exception processing http \ request {0}".format(request.path)) return response
class PipeJsonRpcReceive: """ The class contains functions for receiving and processing JSON RPC messages received on communication pipe. Parameters ---------- conn: multiprocessing.Connection Reference to bidirectional end of a pipe (multiprocessing.Pipe) name: str Name of the receiving thread (it is better to assign meaningful unique names to threads. Examples -------- .. code-block:: python conn1, conn2 = multiprocessing.Pipe() pc = PipeJsonRPC(conn=conn1, name="RE QServer Receive") def func(): print("Testing") pc.add_handler(func, "some_method") pc.start() # Wait and process commands # The function 'func' is called when the message with method=="some_method" is received pc.stop() # Stop before exit to stop the thread. """ def __init__(self, conn, *, name="RE QServer Comm"): self._conn = conn self._dispatcher = Dispatcher() # json-rpc dispatcher self._thread_running = False # Set True to exit the thread self._thread_name = name self._conn_polling_timeout = 0.1 # in sec. def start(self): """ Start processing of the pipe messages """ self._start_conn_thread() def stop(self): """ Stop processing of the pipe messages (and exit the tread) """ self._thread_running = False def __del__(self): self.stop() def add_method(self, handler, name=None): """ Add method to json-rpc dispatcher. Parameters ---------- handler: callable Reference to a handler name: str, optional Name to register (default is the handler name) """ # Add method to json-rpc dispatcher self._dispatcher.add_method(handler, name) def _start_conn_thread(self): if not self._thread_running: # Clear the pipe from outdated unprocessed messages. while self._conn.poll(): self._conn.recv() self._thread_running = True self._thread_conn = threading.Thread( target=self._receive_conn_thread, name=self._thread_name, daemon=True) self._thread_conn.start() def _receive_conn_thread(self): while True: if self._conn.poll(self._conn_polling_timeout): try: msg = self._conn.recv() # Messages should be handled in the event loop self._conn_received(msg) except Exception as ex: logger.exception( "Exception occurred while waiting for RE Manager-> Watchdog message: %s", str(ex)) break if not self._thread_running: # Exit thread break def _conn_received(self, msg): # if logger.level < 11: # Print output only if logging level is DEBUG (10) or less # msg_json = json.loads(msg) # We don't want to print 'heartbeat' messages # if not isinstance(msg_json, dict) or (msg_json["method"] != "heartbeat"): # logger.debug("Command received RE Manager->Watchdog: %s", pprint.pformat(msg_json)) response = JSONRPCResponseManager.handle(msg, self._dispatcher) if response: response = response.json self._conn.send(response)
class BaseJRPCListener(resource.Resource): """ BaseJRPCListener Class is comprised of HTTP interface which listens for the end user requests using JRPC. """ # The isLeaf instance variable describes whether or not a resource will # have children and only leaf resources get rendered. # BaseListener is the supposed to be one but last class in the derivation # tree. So, isLeaf is set to False. isLeaf = False # ----------------------------------------------------------------- def __init__(self, rpc_methods): self.dispatcher = Dispatcher() for m in rpc_methods: self.dispatcher.add_method(m) def _process_request(self, input_json_str): """ Handles incoming requests or rather dispatches to appropriate rpc method Parameters : input_json_str - JSON formatted str of the request Returns : response - data field from the response received which is a dict """ response = {} response['error'] = {} response['error']['code'] = \ JRPCErrorCodes.INVALID_PARAMETER_FORMAT_OR_VALUE try: input_json = json.loads(input_json_str) logger.info("Received request: %s", input_json['method']) except Exception as err: logger.error("exception loading Json: %s", str(err)) response["error"]["message"] = "Improper Json request" return response # save the full json for WorkOrderSubmit input_json["params"]["raw"] = input_json_str data = json.dumps(input_json).encode('utf-8') response = JSONRPCResponseManager.handle(data, self.dispatcher) return response.data def render_GET(self, request): """ Handle a GET request to the listener. Not supported. So only error is expected to be returned as response. Parameters : request - Request coming in from a client Returns : response - A dict type response which always contains error """ # JRPC response with id 0 is returned because id parameter # will not be found in GET request response = jrpc_utility.create_error_response( JRPCErrorCodes.INVALID_PARAMETER_FORMAT_OR_VALUE, "0", "Only POST request is supported") logger.error( "GET request is not supported. Only POST request is supported") return response def render_POST(self, request): """ Handle a POST request to the listener. Decode and delegate to _process_request for handling. Parameters : request - Request coming in from a client Returns : response - A dict type response """ response = {} logger.info('Received a new request from the client') try: # process the message encoding encoding = request.getHeader('Content-Type') data = request.content.read() if encoding == 'application/json': try: input_json_str = data.decode('utf-8') input_json = json.loads(input_json_str) jrpc_id = input_json["id"] response = self._process_request(input_json_str) except AttributeError: logger.error("Error while loading input json") response = jrpc_utility.create_error_response( JRPCErrorCodes.UNKNOWN_ERROR, jrpc_id, "UNKNOWN_ERROR: Error while loading input JSON file") return response else: # JRPC response with 0 as id is returned because id can't be # fetched from a request with unknown encoding. response = jrpc_utility.create_error_response( JRPCErrorCodes.UNKNOWN_ERROR, 0, "UNKNOWN_ERROR: unknown message encoding") return response except Exception as err: logger.exception('exception while decoding http request %s: %s', request.path, str(err)) # JRPC response with 0 as id is returned because id can't be # fetched from improper request response = jrpc_utility.create_error_response( JRPCErrorCodes.UNKNOWN_ERROR, 0, "UNKNOWN_ERROR: unable to decode incoming request") return response # send back the results try: if encoding == 'application/json': response = json.dumps(response) logger.info('response[%s]: %s', encoding, response) request.setHeader('content-type', encoding) request.setResponseCode(http.OK) return response.encode('utf8') except Exception as err: logger.exception( 'unknown exception while processing request %s: %s', request.path, str(err)) response = jrpc_utility.create_error_response( JRPCErrorCodes.UNKNOWN_ERROR, jrpc_id, "UNKNOWN_ERROR: unknown exception processing http " + "request {0}: {1}".format(request.path, str(err))) return response def start(self, host_name, port): """ Start the listener instance on specified socket. Parameters : host_name - The hostname where this listener is reachable port - The port at which this listener needs to listen """ root = self site = server.Site(root) reactor.listenTCP(port, site, interface=host_name) logger.info('%s started on port %s', type(self).__name__, port) try: reactor.run() except reactor_error.ReactorNotRunning: logger.error('shutdown') except Exception as err: logger.error('shutdown: %s', str(err)) exit(0)
class TCSListener(resource.Resource): """ TCSListener Class is comprised of HTTP interface which listens for the end user requests, Worker Registry Handler, Work Order Handler and Work Order Receipts Handler . """ # The isLeaf instance variable describes whether or not a resource will # have children and only leaf resources get rendered. # TCSListener is the most derived class hence isLeaf is required. isLeaf = True # ----------------------------------------------------------------- def __init__(self, config): try: self.kv_helper = connector.open(config['KvStorage']['remote_url']) except Exception as err: logger.error(f"failed to open db: {err}") sys.exit(-1) self.worker_registry_handler = TCSWorkerRegistryHandler(self.kv_helper) self.workorder_handler = TCSWorkOrderHandler( self.kv_helper, config["Listener"]["max_work_order_count"]) self.workorder_receipt_handler = TCSWorkOrderReceiptHandler( self.kv_helper) self.worker_encryption_key_handler = WorkerEncryptionKeyHandler( self.kv_helper) self.dispatcher = Dispatcher() rpc_methods = [ self.worker_encryption_key_handler.EncryptionKeyGet, self.worker_encryption_key_handler.EncryptionKeySet, self.worker_registry_handler.WorkerLookUp, self.worker_registry_handler.WorkerLookUpNext, self.worker_registry_handler.WorkerRegister, self.worker_registry_handler.WorkerSetStatus, self.worker_registry_handler.WorkerRetrieve, self.worker_registry_handler.WorkerUpdate, self.workorder_handler.WorkOrderSubmit, self.workorder_handler.WorkOrderGetResult, self.workorder_receipt_handler.WorkOrderReceiptCreate, self.workorder_receipt_handler.WorkOrderReceiptUpdate, self.workorder_receipt_handler.WorkOrderReceiptRetrieve, self.workorder_receipt_handler.WorkOrderReceiptUpdateRetrieve, self.workorder_receipt_handler.WorkOrderReceiptLookUp, self.workorder_receipt_handler.WorkOrderReceiptLookUpNext ] for m in rpc_methods: self.dispatcher.add_method(m) def _process_request(self, input_json_str): response = {} response['error'] = {} response['error']['code'] = \ WorkOrderStatus.INVALID_PARAMETER_FORMAT_OR_VALUE try: input_json = json.loads(input_json_str) except Exception as err: logger.exception("exception loading Json: %s", str(err)) response = { "error": { "code": WorkOrderStatus.INVALID_PARAMETER_FORMAT_OR_VALUE, "message": "Error: Improper Json. Unable to load", }, } return response logger.info("Received request: %s", input_json['method']) # save the full json for WorkOrderSubmit input_json["params"]["raw"] = input_json_str data = json.dumps(input_json).encode('utf-8') response = JSONRPCResponseManager.handle(data, self.dispatcher) return response.data def render_GET(self, request): # JRPC response with id 0 is returned because id parameter # will not be found in GET request response = jrpc_utility.create_error_response( WorkOrderStatus.INVALID_PARAMETER_FORMAT_OR_VALUE, "0", "Only POST request is supported") logger.error( "GET request is not supported. Only POST request is supported") return response def render_POST(self, request): response = {} logger.info('Received a new request from the client') try: # process the message encoding encoding = request.getHeader('Content-Type') data = request.content.read() if encoding == 'application/json': try: input_json_str = data.decode('utf-8') input_json = json.loads(input_json_str) jrpc_id = input_json["id"] response = self._process_request(input_json_str) except AttributeError: logger.error("Error while loading input json") response = jrpc_utility.create_error_response( WorkOrderStatus.UNKNOWN_ERROR, jrpc_id, "UNKNOWN_ERROR: Error while loading input JSON file") return response else: # JRPC response with 0 as id is returned because id can't be # fetched from a request with unknown encoding. response = jrpc_utility.create_error_response( WorkOrderStatus.UNKNOWN_ERROR, 0, "UNKNOWN_ERROR: unknown message encoding") return response except Exception as err: logger.exception('exception while decoding http request %s: %s', request.path, str(err)) # JRPC response with 0 as id is returned because id can't be # fetched from improper request response = jrpc_utility.create_error_response( WorkOrderStatus.UNKNOWN_ERROR, 0, "UNKNOWN_ERROR: unable to decode incoming request") return response # send back the results try: if encoding == 'application/json': response = json.dumps(response) logger.info('response[%s]: %s', encoding, response) request.setHeader('content-type', encoding) request.setResponseCode(http.OK) return response.encode('utf8') except Exception as err: logger.exception( 'unknown exception while processing request %s: %s', request.path, str(err)) response = jrpc_utility.create_error_response( WorkOrderStatus.UNKNOWN_ERROR, jrpc_id, "UNKNOWN_ERROR: unknown exception processing http " + "request {0}: {1}".format(request.path, str(err))) return response