Exemple #1
0
    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
Exemple #2
0
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()
Exemple #3
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)
        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)
Exemple #6
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