def __init__(self, config): """Init the Pika AMQP 0.9.1 wrapper client. :type config: BaseAMQPClientConfig :param config: The configuration for the AMQP client. """ # Define potential credentials if config.username: from Cerebrum.Utils import read_password cred = pika.credentials.PlainCredentials( config.username, read_password(config.username, config.hostname)) ssl_opts = None else: raise ClientErrors.ConfigurationFormatError( "Configuration contains neither 'username' or 'cert' value") # Create connection-object try: self.conn_params = pika.ConnectionParameters( host=config.hostname, port=config.port, virtual_host=config.virtual_host, credentials=cred, ssl=config.tls_on, ssl_options=ssl_opts) except Exception as e: raise ClientErrors.ConnectionError( 'Invalid connection parameters: {}'.format(e)) self.channel = self.connection = None
def publish(self, messages, durable=True): """Publish a message to the exchange. :type messages: dict or list of dicts. :param messages: The message(s) to publish. :type durable: bool :param durable: If this message should be durable. """ if isinstance(messages, (dict, scim.Event)): messages = [messages] elif not isinstance(messages, list): raise TypeError('messages must be a dict, event or a list thereof') for message in messages: if not isinstance(message, (dict, scim.Event)): raise TypeError('messages must be a dict, ' 'Event or a list thereof') try: err_msg = 'Could not generate routing key' if isinstance(message, dict): if 'routing-key' in message: event_type = message['routing-key'] else: event_type = 'unknown' payload = message else: event_type = message.key payload = message.get_payload() msg_body = dict( filter(lambda x: x[0] != 'routing-key', payload.items())) err_msg = ('Could not generate' ' application/json content from message') msg_body = json.dumps(msg_body) except Exception as e: raise ClientErrors.MessageFormatError('{0}: {1}'.format( err_msg, e)) try: if self.channel.basic_publish( exchange=self.exchange, routing_key=event_type, body=msg_body, properties=pika.BasicProperties( # Delivery mode: # 1: Non-persistent # 2: Persistent delivery_mode=2, content_type='application/json'), # Makes publish return false if # the message is not routed / published mandatory=False, # TODO: Should we enable immediate? ): return True else: raise Exception('Broker did not confirm message delivery') except Exception as e: raise ClientErrors.MessagePublishingError( 'Unable to publish message: {0!r}'.format(e))
def open(self): """Open connection""" try: self.connection = pika.BlockingConnection(self.conn_params) except Exception as e: raise ClientErrors.ConnectionError( 'Unable to connect to broker: {}'.format(e)) # Set up channel self.channel = self.connection.channel()
def publish(self, routing_key, message, durable=True): """Publish a message to the exchange. :type messages: dict or list of dicts. :param messages: The message(s) to publish. :type durable: bool :param durable: If this message should be durable. """ try: routing_key = routing_key or 'unknown' if isinstance(message, basestring): # Validate any previously stored json strings? # message = json.loads(message) msg_body = six.text_type(message) else: msg_body = json.dumps(message) except Exception as e: raise ClientErrors.MessageFormatError( 'Unable to format message: {0!r}'.format(e)) try: if self.channel.basic_publish( exchange=self.exchange_name, routing_key=routing_key, body=msg_body, properties=pika.BasicProperties( delivery_mode=DELIVERY_PERSISTENT, content_type=CONTENT_TYPE), # Makes publish return false if # the message is not routed / published mandatory=False): return True else: raise Exception('Broker did not confirm message delivery') except Exception as e: raise ClientErrors.MessagePublishingError( 'Unable to publish message: {0!r}'.format(e))
def publish(self, messages, durable=True): """ Publish a message to the exchange after scheduling it in case it is marked for scheduling :type messages: dict, scim.Event or list of dicts and / or scim.Event :param messages: The message(s) to publish :type durable: bool :param durable: If this message should be durable :rtype: dict :return: A dict of jti:(celery.result.AsyncResult, eta) """ from Cerebrum.modules.celery_tasks.apps.scheduler import schedule_message super(SchedulingAndPublishingAMQP091Client, self).publish(messages, durable) if isinstance(messages, (dict, scim.Event)): messages = [messages] elif not isinstance(messages, list): raise TypeError('messages must be a dict, event or a list thereof') result_tickets = dict() for message in messages: if not isinstance(message, (dict, scim.Event)): raise TypeError('messages must be a dict, ' 'Event or a list thereof') try: if isinstance(message, dict): err_msg = 'Could not extract schedule time' if 'nbf' not in message: continue eta = datetime.datetime.fromtimestamp(int(message['nbf'])) jti = message.get('jti', 'invalid') err_msg = 'Could not get routing-key' routing_key = message.get('routing-key', 'unknown') err_msg = 'Could not produce message-body' body = json.dumps( dict( filter(lambda x: x[0] != 'routing-key', message.items()))) else: # scim.Event err_msg = 'Could not extract schedule time' if not isinstance(message.scheduled, datetime.datetime): continue eta = message.scheduled jti = message.jti or 'invalid' err_msg = 'Could not get routing-key' routing_key = message.key err_msg = 'Could not produce message-body' body = json.dumps(message.get_payload()) err_msg = 'Could not schedule / produce task' result_tickets[jti] = (schedule_message.apply_async(kwargs={ 'routing_key': routing_key, 'body': body }, eta=eta), eta) except Exception as e: raise ClientErrors.MessageFormatError('{0}: {1}'.format( err_msg, e)) return result_tickets