Exemple #1
0
    def publish_message(self, message, token):
        """ send a message to a specific device. (1:1) """
        conn = self._get_blocking_amqp_conn()
        try:
            channel = conn.channel()

            logger.debug("Declaring incoming exchange '%s'",
                    self.incoming_exchange_name)
            channel.exchange_declare(
                exchange=self.incoming_exchange_name,
                durable=True,
                type='direct',
            )

            logger.debug("Publishing message to exchange '%s'",
                    self.incoming_exchange_name)
            channel.basic_publish(
                exchange=self.incoming_exchange_name,
                routing_key=token,
                body=message,
                properties=pika.BasicProperties(
                    content_type="text/plain",
                ),
            )
        except:
            logger.error("Error publishing message to exchange")
            raise
        finally:
            logger.debug("Disconnecting from message broker")
            conn.disconnect()
Exemple #2
0
    def queue_message(self, message, queue_name):
        """ send a message to a specific outbound queue (1:1) """
        conn = self._get_blocking_amqp_conn()
        try:
            channel = conn.channel()

            logger.debug("Declaring queue '%s' on message broker",
                    queue_name)
            channel.queue_declare(queue=queue_name)

            logger.debug("Sending message to queue '%s' for processing",
                    queue_name)
            channel.basic_publish(
                exchange='',
                routing_key=queue_name,
                body=message,
                properties=pika.BasicProperties(
                    content_type="text/plain",
                ),
            )
        except:
            logger.error("Error queueing message in message broker")
            raise
        finally:
            logger.debug("Disconnecting from message broker")
            conn.disconnect()
Exemple #3
0
    def get_pending_messages(self, username = None):
        result = []
        if username is None:
            return result
        username = str(username)
        connection = self._get_blocking_amqp_conn()
        try:
            channel = connection.channel()
            logger.debug("Connecting to %s ", username)

            channel.queue_declare(queue = username,
                                  durable = True,
                                  exclusive = False,
                                  auto_delete = False)

            while connection.is_open:
                method, header, body = channel.basic_get(queue = username)
                if method.NAME == 'Basic.GetEmpty':
                    # queue empty, stop gathering.
                    connection.close()
                    return result
                else:
                    result.append(body)
                #channel.basic_ack(delivery_tag=method.delivery_tag)
        except Exception, e:
            logger.error("Crapsticks: %s" % str(e))
Exemple #4
0
    def send_broadcast(self, message, username):
        """ Send a message to all queues associated
        with the username exchange (1:N)"""
        user_exchange_name = username
        conn = self._get_blocking_amqp_conn()
        try:
            channel = conn.channel()

            logger.debug("Declaring exchange '%s'", user_exchange_name)
            channel.exchange_declare(
                exchange=user_exchange_name,
                durable=True,
                type='fanout',
            )

            # Send message to user exchange so all other clients receive it
            logger.debug("Publishing message to exchange '%s'",
                user_exchange_name)
            channel.basic_publish(
                exchange=user_exchange_name,
                routing_key='',
                body=message,
                properties=pika.BasicProperties(
                    content_type='text/plain',
                ),
            )
        except:
            logger.error("Error sending broadcast message")
            raise
        finally:
            logger.debug("Closing AMQP connection to broker")
            conn.disconnect()
Exemple #5
0
    def send_broadcast(self, 
                message, 
                username, 
                queue = None, 
                origin = None):
        """ append message to user's out queue
            queue = user/_tok/en_as_path/new_message_token
        """
        if message is None or username is None:
            raise NotifStorageException("incomplete storage request")
        max_msgs = int(self.config.get('redis.max_msgs_per_user', '200'))
        file_ok = False
        doc_path = self._user_storage_path(username)
        if queue is None:
            queue = origin;

        if not os.path.exists(doc_path):
            os.makedirs(doc_path)
        try:
            while (not file_ok):
                doc_file = new_token()
                file_ok = not os.path.isfile(os.path.join(doc_path, doc_file))
            file_path = os.path.join(doc_path, doc_file)
            file = os.open(file_path, os.O_WRONLY | os.O_CREAT)
            os.write(file, message)
            os.close(file)
        except IOError, e:
            logger.error("Could not write message file %s" % str(e))
            raise NotifStorageException("Error storing message content")
Exemple #6
0
    def _ensure_exchanges_exist(self, incoming_exchange_name,
            user_exchange_name):
        conn = self._get_blocking_amqp_conn()
        try:
            channel = conn.channel()

            # TODO: Decide if we should remove this call; the incoming exchange
            # should always exist before new_subscription is called
            logger.debug("Declaring incoming exchange %s",
                    incoming_exchange_name)
            channel.exchange_declare(
                exchange=incoming_exchange_name,
                durable=True,
                type='direct',
            )

            # TODO: Decide if we should remove this call; the user exchange
            # should always exist before new_subscription is called
            logger.debug("Declaring user exchange %s", user_exchange_name)
            channel.exchange_declare(
                exchange=user_exchange_name,
                durable=True,
                type='fanout',
            )
        except:
            logger.error("Error ensuring exchanges exist on broker")
            raise
        finally:
            logger.debug("Closing AMQP connection to broker")
            conn.disconnect()
Exemple #7
0
    def _delete_binding(self, source_exch, dest_exch, routing_key):
        http_conn = self._get_http_conn()
        try:
            auth_headers = {
                'Authorization': 'Basic ' +
                    base64.b64encode('%s:%s' % \
                        (self.broker_user, self.broker_pass)
                    ),
            }

            path = '/api/bindings/%s/e/%s/e/%s/%s' % (
                urllib.quote_plus(self.broker_vhost),
                urllib.quote_plus(source_exch),
                urllib.quote_plus(dest_exch),
                # Encode twice because binding "properties" are URL-encoded before stored
                urllib.quote_plus(urllib.quote_plus(routing_key))
            )

            logger.debug("Sending HTTP DELETE request to broker %s", path)
            http_conn.request('DELETE', path, '', auth_headers)

            response = http_conn.getresponse()
            logger.debug("Broker returned response with status %s", response.status)

            if response.status != httplib.NO_CONTENT:
                logger.error("Unexpected response status '%s'", response.status)
                raise Exception("Unexpected response status '%s'", response.status)
        except:
            logger.error("Error deleting binding via HTTP")
            raise
        finally:
            logger.debug("Closing HTTP connection to broker")
            http_conn.close()
Exemple #8
0
 def _resolve_token(self, token):
     try:
         mapping = self.db.mapping.get({u'token': token,
                                        u'type': 'sub'})
         if mapping is None or mapping.get('user', None) is None:
             return None
         return mapping.get('user')
     except OperationFailure, e:
         logger.error('Could not find mapping to user for token %s, %s',
                      token, str(e))
         return None
Exemple #9
0
 def delete_subscription(self, username, token):
     try:
         self.db.mapping.remove({u'token': token,
                                 u'user_id': username})
         self.db.user.remove({'user_id': username,
                              'origin': token})
         return True
     except OperationFailure, e:
         logger.error('Could not remove mappning for subscription %s'
                      % str(e))
         return False
Exemple #10
0
    def create_subscription(self, username, token):
        """Map token to username"""
        mapping_info = {u'token': token,
                        u'user_id': username,
                        u'type': 'sub',
                        u'created': int(time.time())}

        try:
            self.db.mapping.insert(mapping_info, safe=True)
            return True
        except OperationFailure, e:
            logger.error('Could not create subscription: %s' % str(e))
            return False
Exemple #11
0
 def user_info(self, username, user_info = None):
     try:
         if user_info is None:
             # Fetch the user's information
             user_info = self.redis.get('ui:%s' % username)
             if user_info is not None:
                 return json.loads(user_info)
             else:
                 return {}
         else:
             self.redis.set('ui:%s' % username, json.dumps(user_info))
             return user_info
     except Exception, e:
         logger.error("Could not access user info %s, %s" % (username,
                                                             str(e)))
         raise
Exemple #12
0
 def send_broadcast(self, message, username, origin = None):
     msg_content = {}
     ttl = int(self.config.get('notif_server.max_ttl_seconds',
                               '259200'))  # 3 days
     try:
         msg_content = json.loads(message)
         ttl = msg_content.get('ttl', ttl)
         self.db.message.save({u'user_id': username,
                        'origin': origin,
                        'message': message,
                        'expry': int(time.time() + ttl)})
         #TODO:: Add in notification to tickle listening
         # clients (if desired)
     except OperationFailure, e:
         logger.error("Could not save message to %s from %s " % (
             username, origin
         ))
Exemple #13
0
    def handle_deliveries(self, config=None,
                          username=None,
                          callback=None):
        if username is None:
            return

        handler = {'username': username,
                   'callback': callback}

        config['handler'] = handler

        try:

            class Deliv_Handler:
                def __init__(self,
                             config=config):
                    self.params = config

                def on_connected(self, connection):
                    connection.channel(self.on_channel_open)

                def on_channel_open(self, channel):
                    self.channel = channel
                    self.channel.queue_declare(queue=
                                           self.config.get('channel_name'),
                                           durable=True,
                                           exclusive=False,
                                           auto_delete=False,
                                           callback=self.queue_declared)

                def on_queue_declared(self, frame):
                    self.channel.basic_consume(self.handle_delivery,
                             queue = self.config.get('channel_name'))

                def handle_delivery(self, channel, method, header, body):
                    # publish the item.
                    # print body
                    callback(channel=channel,
                             method=method,
                             header=header,
                             body=body)

        except Exception, e:
            logger.error("Unhandled exception %s", str(e))
            raise
Exemple #14
0
    def create_client_queue(self, username):
        user_exchange_name = username

        # Create a unique client id to also be used as the name of the queue
        client_queue_name = "%x" % random.getrandbits(256)

        logger.info("Creating queue %s for user %s", client_queue_name, username)

        # Create the user exchange (if it doesn't already exist) and a client
        # queue with the generated name.
        conn = self._get_amqp_conn()
        try:
            channel = conn.channel()

            logger.debug("Declaring exchange %s", user_exchange_name)
            channel.exchange_declare(
                exchange=user_exchange_name,
                durable=True,
                type='fanout',
            )

            logger.debug("Declaring queue %s", client_queue_name)
            channel.queue_declare(queue=client_queue_name, durable=True)

            logger.debug("Binding queue %s to exchange %s",
                client_queue_name,
                user_exchange_name,
            )
            channel.queue_bind(
                exchange=user_exchange_name,
                queue=client_queue_name,
            )
        except:
            logger.error("Error creating new client queue")
            raise
        finally:
            logger.debug("Closing AMQP connection to broker")
            conn.disconnect()

        return {
            'queue_id': client_queue_name,
            'host': self.broker_host,
            'port': self.broker_amqp_port,
        }
Exemple #15
0
    def _add_binding(self, source_exch, dest_exch, routing_key):
        # XXX: OH EM GEE this is a hack. Creating Exchange-to-exchange (E2E)
        # bindings is a RabbitMQ-specific extension, so the pika AMQP
        # client doesn't support it. The RabbitMQ REST API does however,
        # so we do a quick HTTP POST here to create the binding.
        #
        # To do this properly, we'll probably have to roll our own version
        # of Pika which supports the exchange.bind method call.
        http_conn = self._get_http_conn()
        try:
            auth_headers = {
                'Authorization': 'Basic ' +
                    base64.b64encode('%s:%s' % \
                        (self.broker_user, self.broker_pass)
                    ),
                'Content-Type': 'application/json',
            }

            path = '/api/bindings/%s/e/%s/e/%s' % (
                urllib.quote_plus(self.broker_vhost),
                urllib.quote_plus(source_exch),
                urllib.quote_plus(dest_exch)
            )

            body = json.dumps({'routing_key': routing_key, 'arguments': []})

            logger.debug("Sending HTTP POST request to broker %s", path)
            http_conn.request('POST', path, body, auth_headers)

            response = http_conn.getresponse()
            logger.debug("Broker returned response with status %s",
                    response.status)

            if response.status != httplib.CREATED:
                logger.error("Unexpected response status '%s'",
                        response.status)
                raise Exception("Unexpected response status '%s'",
                        response.status)
        except:
            logger.error("Error adding binding via HTTP")
            raise
        finally:
            logger.debug("Closing HTTP connection to broker")
            http_conn.close()
Exemple #16
0
 def create_client_queue(self, username):
     logger.info("Creating incoming queue for user %s" % username)
     try:
         # Check to see if the user already has a token.
         # ut: user -> token
         user_token = self.redis.get('u2t:%s' % username)
         if user_token is None:
             user_token = new_token()
             self.redis.set('u2t:%s' % username, user_token)
             self.redis.set('t2u:%s' % user_token, username)
             info = {"created": int(time.time()),
                  "state": 'active',
                  "changedate": int(time.time())}
             self.redis.hmset('sin:%s:%s' % (username, user_token), info)
         return {'queue_id': user_token,
                 'port': self.config.get('notifserver.port', 80),
                 'host': self.config.get('notifserver.host')}
     except Exception, ex:
         logger.error("Could not create user queue %s" % str(ex))
Exemple #17
0
    def create_client_queue(self, username):
        # create the mapping record

        channel_info = {u'token': new_token(),
                        u'user_id': username,
                        u'type': 'queue',
                        u'created': int(time.time())
                        }

        logger.info("Creating incoming queue %s for user %s" % (
                    channel_info.get('token'),
                    username))
        try:
            self.db.user.insert(channel_info, safe=True)
            return {'queue_id': channel_info['token'],
                    'host': self.config.get('notifserver.host'),
                    'port': self.config.get('notifserver.port')}

        except OperationFailure, e:
            logger.error('Could not create mapping: %s' % str(e))
            return False
Exemple #18
0
    def create_client_queue(self, username):
        self.channel_info = {}
        self.channel_info['exchange_name'] = username
        self.channel_info['queue_name'] = new_token()

        # Create a unique client id to also be used as the name of the queue
        client_queue_name = new_token()

        logger.info("Creating queue %s for user %s",
                        self.channel_info.get('queue_name'),
                        self.channel_info.get('exchange_name'))

        # Create the user exchange (if it doesn't already exist) and a client
        # queue with the generated name.
        conn = self._get_blocking_amqp_conn()
        try:
            channel = conn.channel()

            logger.debug("Declaring exchange %s",
                        self.channel_info.get('exchange_name'))
            channel.exchange_declare(
                exchange=self.channel_info.get('exchange_name'),
                durable=True,
                type='fanout',
            )

            logger.debug("Declaring queue %s", client_queue_name)
            channel.queue_declare(queue=client_queue_name, durable=True)

            logger.debug("Binding queue %s to exchange %s",
                client_queue_name,
                self.channel_info.get('exchange_name'),
            )
            channel.queue_bind(
                exchange=self.channel_info.get('exchange_name'),
                queue=client_queue_name,
            )
        except Exception, e:
            logger.error("Error creating new client queue. %s" % e.message)
            raise NotifStorageException('Cannot create new client')
Exemple #19
0
    def create_subscription(self, username, token, origin = None):
        """ Map a token to a username """
        # s2u: subscription to user
        # u2s: user subscriptions

        if username is None or token is None:
            return {}
        retObj = {'queue_id': token,
                'port': self.config.get('notifserver.port', 80),
                'host': self.config.get('notifserver.host')}
        prevToken = self.redis.get('ssu:%s:%s' % (username, origin))
        if prevToken is not None:
            self.reactivate_subscription(username, prevToken, origin)
            token = prevToken
            retObj['queue_id'] = token
            return retObj
        mappedToken = self.redis.get("s2u:%s" % token)
        if mappedToken is not None:
            if self.redis.get("s2u:%s" % token) == username:
                return retObj
            logger.error("Token collision! %s" % token)
            return False
        self.redis.set("s2u:%s" % token, username)
        self.redis.set("ssu:%s:%s" % (username, origin), token)
        user_tokens = self.redis.lrange("u2s:%s" % username, 0, -1)
        if token not in user_tokens:
            self.redis.lpush("u2s:%s" % username, token)
            info = {"created": int(time.time()),
                     "state": 'active',
                     "changedate": int(time.time())}
            self.redis.hmset("sin:%s:%s" % (username, token), info)
            print ("Created sin:%s:%s" % (username, token))
            if origin is not None:
                self.redis.hset("sin:%s:%s" % (username, token),
                        "origin", origin)
        return retObj
Exemple #20
0
                                  '259200'))  # 3 days
        try:
            msg_content = json.loads(message)
            ttl = msg_content.get('ttl', ttl)
            self.db.message.save({u'user_id': username,
                           'origin': origin,
                           'message': message,
                           'expry': int(time.time() + ttl)})
            #TODO:: Add in notification to tickle listening
            # clients (if desired)
        except OperationFailure, e:
            logger.error("Could not save message to %s from %s " % (
                username, origin
            ))
        except ValueError, e:
            logger.error("message is not valid JSON, ignoring %s" %
                         str(e))
            return False

    def get_pending_messages(self, username = None, since = None):
        result = []
        if username is None:
            return result
        query = {u'user_id': username}
        # TODO: set a default 'since' ?
        if since is not None:
            query['ttl'] = {'$gt': since}
        return list(self.db.user.find(query))

    def _purge(self):
        try:
            self.db.remove({'expry': {'$lt': int(time.time())}})
Exemple #21
0
 def _purge(self):
     try:
         self.db.remove({'expry': {'$lt': int(time.time())}})
     except OperationFailure, e:
         logger.error("Could not purge old messages: %s", str(e))
Exemple #22
0
 def _get_blocking_amqp_conn(self):
     try:
         return pika.BlockingConnection(self.conn_params)
     except Exception, e:
         logger.error("Could not connect to amqp server %s", e.message)
         raise NotifStorageException("Could not connect to server")