コード例 #1
0
    def occur_in_response_to(clazz: str, action: str, payload: object,
                             result: Result, n_priors: int) -> int:
        """
        Evaluates the result of an action and performs additional actions as necessary

        Parameters
        ----------
        clazz: str
            The class name that implemented the method handling the action
        action: str
            The name of the method implemented by clazz that was called
        payload: object
            The argument that were passed to the method
        result: Result
            The result of the method call
        n_priors: int
            A value indicating the number of reactions that have occurred during the requested operation. I.e., this
            value is set to 0 as the router begins calling methods of each manager implementing the current request
            action, and it increases by one each time a Result from a manager operation triggers a reaction.
        """
        global config

        if result.is_error():
            send_email(
                config.get("REACTION_NOTIFY"), "metaroot operation failed",
                "<table>" + "<tr><td>Class</td><td>" + clazz + "</td></tr>"
                "<tr><td>Action</td><td>" + action + "</td></tr>" +
                "<tr><td>Payload</td><td>" + str(payload) + "</td></tr>" +
                "<tr><td>Result Status</td><td>" + str(result.status) +
                "</td></tr>" + "<tr><td>Result Payload</td><td>" +
                str(result.response) + "</td></tr>" + "</table>")
        return 0
コード例 #2
0
ファイル: server.py プロジェクト: cwru-rcci/metaroot
    def call_method(self, obj: object, message: dict):
        """
        Calls a method of an object

        Parameters
        ----------
        obj: object
            An object to invoke a method of
        message: dict
            Parameters specifying the method to invoke and arguments to pass

        Returns
        ----------
        dict
            key status is 0 for success, and >0 on error
            key response is response from method call, or None is server is returning internal error
        """
        # Validate that the message defines an 'action' attribute which maps to a method name
        if 'action' not in message:
            self._logger.error("The message does not define an 'action' -> %s",
                               message)
            return self.get_error_response(450)

        # Lookup the requested method in the handler object
        try:
            method = getattr(obj, message['action'])
        except AttributeError:
            self._logger.error(
                "The method %s is not defined on the argument object %s",
                message['action'],
                type(obj).__name__)
            return self.get_error_response(451)

        # Validate arguments match the method signature
        arguments = inspect.signature(method).parameters
        args = []
        for argument in arguments:
            if argument not in message:
                self._logger.error(
                    "Call to method %s.%s%s, no parameter %r in message",
                    type(obj).__name__, message['action'],
                    inspect.signature(method), argument)
                return self.get_error_response(452)
            args.append(message[argument])

        # Call the method, returning its Result. This is wrapped by a try/except so that exception raise by method
        # calls do not cause the server to stop
        try:
            return method(*args).to_transport_format()
        except Exception as e:
            self._logger.exception(e)
            send_email(
                self._config.get("NOTIFY_ON_ERROR"),
                "Method call error: " + self._config.get_mq_handler_class(),
                str(e))
            return self.get_error_response(455)
コード例 #3
0
 def test_send_email_fail_address_unresolveable(self):
     self.assertEqual(
         False, send_email("foo", "test email", "<i>Test Content</i>"))
コード例 #4
0
ファイル: client.py プロジェクト: cwru-rcci/metaroot
    def send(self, obj: object) -> Result:
        """
        Method to initiate an RPC request

        Parameters
        ----------
        obj: object
            A dictionary specifying a remote method name and arguments to invoke

        Returns
        ----------
        Result
            Result.status is 0 for success, >0 on error
            Result.response is any object returned by the remote method invocation or None
        """
        # Encode the request dict as YAML
        try:
            message = yaml.safe_dump(obj)
        except yaml.YAMLError as exc:
            self.logger.error("YAML serialization error: %s", exc)
            self.logger.error("{0}".format(obj))
            return Result(453, None)

        self.response = None
        self.corr_id = str(uuid.uuid4())

        # Send RPC request to server
        not_sent = True
        attempts = 1
        while not_sent and attempts < 10:
            try:
                self.channel.basic_publish(exchange='',
                                           routing_key=self.queue,
                                           body=message,
                                           properties=pika.BasicProperties(
                                               reply_to=self.callback_queue,
                                               correlation_id=self.corr_id))
                not_sent = False
            except Exception as e:
                self.logger.info("Failed to send on attempt %d because connection closed. Reconnecting...", attempts)
                time.sleep((attempts-1)*5)
                if self.connection.is_closed:
                    self.connect()

            attempts = attempts + 1
        if not_sent:
            self.logger.error("Failed to deliver message %s:%s", self.queue, message.rstrip())
            send_email(self.config.get("NOTIFY_ON_ERROR"),
                       "Message delivery failure: " + self.__class__.__name__,
                       "Failed to deliver message {0}:{1}".format(self.queue, message.rstrip()))
            return Result(470, "Message could not be delivered")

        # Wait for response
        attempts = 1
        while self.response is None and attempts < 36:
            self.logger.debug("Waiting for callback response to %s", str(obj))
            # Process events in
            self.connection.process_data_events(time_limit=5)
            attempts = attempts + 1
        self.corr_id = None

        # If timed out waiting for response
        if attempts == 36:
            self.logger.error("Operation timed out waiting for a response to %s:%s", self.queue, message.rstrip())
            send_email(self.config.get("NOTIFY_ON_ERROR"),
                       "RPC timeout failure: " + self.__class__.__name__,
                       "No response received for message {0}:{1}".format(self.queue, message.rstrip()))
            return Result(471, "Operation timed out waiting for a response")

        # Decode the response dict as YAML
        try:
            res_obj = yaml.safe_load(self.response)
            return Result.from_transport_format(res_obj)
        except yaml.YAMLError as exc:
            self.logger.error("YAML serialization error: %s", exc)
            self.logger.error("{0}".format(obj))
            return Result(454, None)
コード例 #5
0
ファイル: server.py プロジェクト: cwru-rcci/metaroot
    def start(self, config_key):
        """
        Instantiates the hosted manager object and consumes messages that map to methods of the manager

        Parameters
        ----------
        config_key: str
            Key identifying which node of the configuration tree to use

        Returns
        ----------
        int
            Returns 1 on exit
        """
        self._config = metaroot.config.get_config(config_key)

        # Setup our custom logging to use the class name processing the messages as its tag
        self._logger = metaroot.utils.get_logger(
            self.__class__.__name__, self._config.get_log_file(),
            self._config.get_file_verbosity(),
            self._config.get_screen_verbosity())

        # Output debug logging
        self._logger.debug("VVVVVV RPCServer Config VVVVVV")
        metaroot.config.debug_config(self._config)

        # Instantiate an instance of the class specified in the config file that will process messages
        self._handler = metaroot.utils.instantiate_object_from_class_path(
            self._config.get_mq_handler_class())
        self._handler.initialize()

        # We want to exit gracefully if a SIGTERM is sent, so configure a handler
        # signal.signal(signal.SIGTERM, self.shutdown)

        # Consume messages, attempting to recover from network dropout
        self._logger.info('starting consume loop for messages of type "%s"...',
                          self._config.get_mq_queue_name())
        connect_attempts = 1
        while connect_attempts < 30 and not self._exit_requested:
            if not self.connect():
                self._logger.info(
                    "Failed to connect on attempt %d. Will try again after sleeping %d seconds",
                    connect_attempts, connect_attempts * 5)
                time.sleep(connect_attempts * 5)
                connect_attempts = connect_attempts + 1
            else:
                self._logger.info(
                    "Connected to message host %s:%d after %d attempts",
                    self._config.get_mq_host(), self._config.get_mq_port(),
                    connect_attempts)
                send_email(
                    self._config.get("NOTIFY_ON_ERROR"),
                    "RPC Server (re)connect for " +
                    self._config.get_mq_handler_class(),
                    "Connected to message host {0}:{1} after {2} attempts".
                    format(self._config.get_mq_host(),
                           self._config.get_mq_port(), connect_attempts))
                connect_attempts = 1

                try:
                    self.start_consuming()
                except KeyboardInterrupt as e:
                    self._logger.warning("Interrupted by keyboard input.")
                    break
                except (pika.exceptions.AMQPConnectionError,
                        pika.exceptions.ConnectionClosed,
                        pika.exceptions.ChannelClosed,
                        pika.exceptions.StreamLostError) as e:
                    self._logger.exception(e)
                    self._logger.error(
                        "Consume loop broken...will attempt to reconnect")
                except Exception as e:
                    self._logger.exception(e)
                    self._logger.error(
                        "Consume loop broken...will NOT attempt to reconnect")
                    send_email(
                        self._config.get("NOTIFY_ON_ERROR"),
                        "RPC Server Exited: " +
                        self._config.get_mq_handler_class(), str(e))
                    break

        return 1