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)