def cleanup(self):
     """Cleans up the connection.
     """
     try:
         if self._connection is not None:
             self._channel.stop_consuming()
             self._channel.close()
             self._connection.close()
     except Exception as e:
         logger.error(f'Error closing RabbitMQ connection. {e}')
 def ack(self, ch, delivery_tag):
     """Acknowledges the message on the channel. Retries on connection failure
     with a new RabbitMQ node.
     """
     try:
         self._channel.basic_ack(delivery_tag)
     except connection_exceptions as e:
         logger.error(f'Connection Ack error.: {repr(e)} Retrying...')
         self._retry_connection()
         self.ack(ch, delivery_tag)
 def _retry_connection(self):
     """Retries to establish the connection until a connection is made
     with RabbitMQ a node in the cluster.
     """
     while True:
         try:
             self._establish_connection()
             logger.info('Connection established with RabbitMQ...')
             break
         except connection_exceptions as e:
             logger.error(connection_error_msg.format(repr(e)))
             time.sleep(self.wait_time)
         except Exception:
             raise
 def publish(self, exchange, routing_key, properties, body):
     """Publishes the message to the channel. If fails retries with
     a new connection.
     """
     try:
         self._channel.basic_publish(
             exchange=exchange,
             routing_key=routing_key,
             properties=properties,
             body=body,
         )
     except connection_exceptions as e:
         logger.error(connection_error_msg.format(e))
         self._retry_connection()
         self.publish(exchange, routing_key, properties, body)
    def consume(self, callback):
        """Consumes based on routing key. Retries if fails."""
        try:
            result = self._channel.queue_declare(queue="", exclusive=True)
            self._channel.queue_bind(
                exchange=self.exchange_name,
                queue=result.method.queue,
                routing_key=self.routing_key,
            )
            self._channel.basic_consume(on_message_callback=callback,
                                        queue=result.method.queue)
            self._channel.start_consuming()

        except connection_exceptions as e:
            logger.error(connection_error_msg.format(e))
            self._retry_connection()
            self.consume(callback)
    def _process_msg(self, ch, method, properties, body):
        """Parses the incoming message and hands off to the appropriate module"""

        self._log_debug("_process_msg, body: %s" % body)

        ingressMsg = {}
        try:
            if isinstance(body, dict) == False:
                ingressMsg = json.loads(body)
            else:
                ingressMsg = body

            # Authenticate message using username and signature fields
            username = ingressMsg.get("username")
            signature = ingressMsg.get("signature")
            message = ingressMsg.get("message")

            assert (username is not None)
            assert (signature is not None)
            assert (message is not None)

            msg_len = len(message) + 1

            if SSPL_SEC.sspl_verify_message(msg_len, str(message), username,
                                            signature) != 0:
                logger.error("Authentication failed on message: %s" %
                             ingressMsg)
                return

            # We're acting as HAlon so ignore actuator_requests
            #  and sensor_requests messages
            if message.get("actuator_request_type") is not None or \
                message.get("sensor_request_type") is not None:
                return

            # Get the message type
            msgType = message.get("actuator_response_type")

            # If it's an incoming actuator msg then validate against
            #  Actuator Response schema
            if msgType is not None:
                validate(ingressMsg, self._actuator_schema)

            if msgType is None:
                msgType = message.get("sensor_response_type")
                validate(ingressMsg, self._sensor_schema)

                # Ignore drive status messages when thread starts up during tests
                if message.get("sensor_response_type").get(
                        "disk_status_drivemanager") is not None:
                    return
            # If the message comes from other SSPL hosts, do not pass that
            # message to internal queue. This happens as SSPL instances are
            # listening to common queues in a RabbitMQ cluster.
            if 'host_id' in msgType and socket.getfqdn() != msgType['host_id']:
                self._connection.ack(ch, delivery_tag=method.delivery_tag)
                return
            # Write to the msg queue so the lettuce tests can
            #  retrieve it and examine for accuracy during automated testing
            self._write_internal_msgQ("RabbitMQingressProcessorTests", message)

            # Acknowledge message was received
            self._connection.ack(ch, delivery_tag=method.delivery_tag)

        except Exception as ex:
            logger.exception("_process_msg unrecognized message: %r" %
                             ingressMsg)
    def _transmit_msg_on_exchange(self):
        """Transmit json message onto RabbitMQ exchange"""
        self._log_debug("_transmit_msg_on_exchange, jsonMsg: %s" % self._jsonMsg)

        try:
            # Check for shut down message from sspl_ll_d and set a flag to shutdown
            #  once our message queue is empty
            if self._jsonMsg.get("message").get("actuator_response_type") is not None and \
                self._jsonMsg.get("message").get("actuator_response_type").get("thread_controller") is not None and \
                self._jsonMsg.get("message").get("actuator_response_type").get("thread_controller").get("thread_response") == \
                    "SSPL-LL is shutting down":
                    logger.info("RabbitMQegressProcessor, _transmit_msg_on_exchange, received" \
                                    "global shutdown message from sspl_ll_d")
                    self._request_shutdown = True

            msg_props = pika.BasicProperties()
            msg_props.content_type = "text/plain"

            # Publish json message to the correct channel
            # NOTE: We need to route ThreadController messages to ACK channel.
            # We can't modify schema as it will affect other modules too. As a
            # temporary solution we have added a extra check to see if actuator_response_type
            # is "thread_controller".
            # TODO: Find a proper way to solve this issue. Avoid changing
            # core egress processor code
            if self._jsonMsg.get("message").get("actuator_response_type") is not None and \
              (self._jsonMsg.get("message").get("actuator_response_type").get("ack") is not None or \
                self._jsonMsg.get("message").get("actuator_response_type").get("thread_controller") is not None):
                self._add_signature()
                jsonMsg = json.dumps(self._jsonMsg).encode('utf8')
                self._ack_connection.publish(exchange=self._exchange_name,
                                             routing_key=self._ack_routing_key,
                                             properties=msg_props,
                                             body=jsonMsg)
            # Routing requests for IEM msgs sent from the LoggingMsgHandler
            elif self._jsonMsg.get("message").get("IEM_routing") is not None:
                log_msg = self._jsonMsg.get("message").get("IEM_routing").get("log_msg")
                self._log_debug("Routing IEM: %s" % log_msg)
                if self._iem_route_addr != "":
                    self._iem_connection.publish(exchange=self._iem_route_exchange_name,
                                                 routing_key=self._routing_key,
                                                 properties=msg_props,
                                                 body=str(log_msg))
                else:
                    logger.warn("RabbitMQegressProcessor, Attempted to route IEM without a valid 'iem_route_addr' set.")

            elif self._jsonMsg.get("message") is not None:
                message = self._jsonMsg.get("message")
                if message.get("actuator_request_type") or \
                    message.get("sensor_request_type") is not None:
                    logger.error("inside egress, test actuator")
                    unique_routing_key = f'{self._routing_key}_node{self._node_id}'
                    logger.info(f"Connecting using routing key: {unique_routing_key}")
                    logger.error(f"Connecting using routing key: {unique_routing_key}")
                    self._add_signature()
                    jsonMsg = json.dumps(self._jsonMsg).encode('utf8')
                    self._connection.publish(exchange=self._exchange_name,
                                            routing_key=unique_routing_key,
                                            properties=msg_props,
                                            body=jsonMsg)

            else:
                self._add_signature()
                jsonMsg = json.dumps(self._jsonMsg).encode('utf8')
                self._connection.publish(exchange=self._exchange_name,
                                         routing_key=self._routing_key,
                                         properties=msg_props,
                                         body=jsonMsg)
            # No exceptions thrown so success
            self._log_debug("_transmit_msg_on_exchange, Successfully Sent: %s" % self._jsonMsg)
            self._msg_sent_succesfull = True

        except Exception as ex:
            logger.exception("RabbitMQegressProcessor, _transmit_msg_on_exchange: %r" % ex)
            self._msg_sent_succesfull = False
    def _read_config(self):
        """Configure the RabbitMQ exchange with defaults available"""
        try:
            self._virtual_host = Conf.get(
                SSPL_TEST_CONF, f"{self.RABBITMQPROCESSOR}>{self.VIRT_HOST}",
                'SSPL')

            # Read common RabbitMQ configuration
            self._primary_rabbitmq_host = Conf.get(
                SSPL_TEST_CONF,
                f"{self.RABBITMQPROCESSOR}>{self.PRIMARY_RABBITMQ_HOST}",
                'localhost')

            # Read RabbitMQ configuration for sensor messages
            self._queue_name = Conf.get(
                SSPL_TEST_CONF, f"{self.RABBITMQPROCESSOR}>{self.QUEUE_NAME}",
                'sensor-queue')
            self._exchange_name = Conf.get(
                SSPL_TEST_CONF,
                f"{self.RABBITMQPROCESSOR}>{self.EXCHANGE_NAME}", 'sspl-out')
            self._routing_key = Conf.get(
                SSPL_TEST_CONF, f"{self.RABBITMQPROCESSOR}>{self.ROUTING_KEY}",
                'sensor-key')
            # Read RabbitMQ configuration for Ack messages
            self._ack_queue_name = Conf.get(
                SSPL_TEST_CONF,
                f"{self.RABBITMQPROCESSOR}>{self.ACK_QUEUE_NAME}",
                'sensor-queue')
            self._ack_routing_key = Conf.get(
                SSPL_TEST_CONF,
                f"{self.RABBITMQPROCESSOR}>{self.ACK_ROUTING_KEY}",
                'sensor-key')

            self._username = Conf.get(
                SSPL_TEST_CONF, f"{self.RABBITMQPROCESSOR}>{self.USER_NAME}",
                'sspluser')
            self._password = Conf.get(
                SSPL_TEST_CONF, f"{self.RABBITMQPROCESSOR}>{self.PASSWORD}",
                '')
            self._signature_user = Conf.get(
                SSPL_TEST_CONF,
                f"{self.RABBITMQPROCESSOR}>{self.SIGNATURE_USERNAME}",
                'sspl-ll')
            self._signature_token = Conf.get(
                SSPL_TEST_CONF,
                f"{self.RABBITMQPROCESSOR}>{self.SIGNATURE_TOKEN}",
                'FAKETOKEN1234')
            self._signature_expires = Conf.get(
                SSPL_TEST_CONF,
                f"{self.RABBITMQPROCESSOR}>{self.SIGNATURE_EXPIRES}", "3600")
            self._iem_route_addr = Conf.get(
                SSPL_TEST_CONF,
                f"{self.RABBITMQPROCESSOR}>{self.IEM_ROUTE_ADDR}", '')
            self._iem_route_exchange_name = Conf.get(
                SSPL_TEST_CONF,
                f"{self.RABBITMQPROCESSOR}>{self.IEM_ROUTE_EXCHANGE_NAME}",
                'sspl-in')
            self._node_id = Conf.get(
                SSPL_TEST_CONF, f"{self.SYSTEM_INFORMATION}>{self.NODE_ID}",
                'SN01')
            cluster_id = Conf.get(
                SSPL_TEST_CONF, f"{self.SYSTEM_INFORMATION}>{self.CLUSTER_ID}",
                'CC01')

            # Decrypt RabbitMQ Password
            decryption_key = encryptor.gen_key(cluster_id,
                                               ServiceTypes.RABBITMQ.value)
            self._password = encryptor.decrypt(decryption_key,
                                               self._password.encode('ascii'),
                                               "RabbitMQegressProcessor")

            if self._iem_route_addr != "":
                logger.info("         Routing IEMs to host: %s" %
                            self._iem_route_addr)
                logger.info("         Using IEM exchange: %s" %
                            self._iem_route_exchange_name)
        except Exception as ex:
            print(ex)
            logger.error("RabbitMQegressProcessor, _read_config: %r" % ex)