Esempio n. 1
0
    def _create_msg(self, queue, msg, client_uuid, project):
        slug = str(uuid.uuid1())
        contents = jsonutils.dumps(
            {'body': msg.get('body', {}), 'claim_id': None,
             'ttl': msg['ttl'], 'claim_count': 0})
        try:
            self._client.put_object(
                utils._message_container(queue, project),
                slug,
                contents=contents,
                content_type='application/json',
                headers={
                    'x-object-meta-clientid': str(client_uuid),
                    'x-delete-after': msg['ttl']})
        except swiftclient.ClientException as exc:
            # NOTE(flwang): To avoid check the queue existence each time when
            # posting messages, let's catch the 404 and create the 'queue'
            # on demand.
            if exc.http_status == 404:
                self._client.put_container(utils._message_container(queue,
                                                                    project))
                self._client.put_object(
                    utils._message_container(queue, project),
                    slug,
                    contents=contents,
                    content_type='application/json',
                    headers={
                        'x-object-meta-clientid': str(client_uuid),
                        'x-delete-after': msg['ttl']})

        return slug
Esempio n. 2
0
 def _delete(self, queue, message_id, project=None):
     try:
         self._client.delete_object(
             utils._message_container(queue, project), message_id)
     except swiftclient.ClientException as exc:
         if exc.http_status != 404:
             raise
Esempio n. 3
0
    def _delete_queue_messages(self, queue, project, pipe):
        """Method to remove all the messages belonging to a queue.

        Will be referenced from the QueueController.
        The pipe to execute deletion will be passed from the QueueController
        executing the operation.
        """
        container = utils._message_container(queue, project)
        remaining = True
        key = ''
        while remaining:
            headers, objects = self._client.get_container(container,
                                                          limit=1000,
                                                          marker=key)
            if not objects:
                return
            remaining = len(objects) == 1000
            key = objects[-1]['name']
            for o in objects:
                try:
                    self._client.delete_object(container, o['name'])
                except swiftclient.ClientException as exc:
                    if exc.http_status == 404:
                        continue
                    raise
Esempio n. 4
0
 def _delete(self, queue, message_id, project=None):
     try:
         self._client.delete_object(
             utils._message_container(queue, project), message_id)
     except swiftclient.ClientException as exc:
         if exc.http_status != 404:
             raise
Esempio n. 5
0
    def _delete_queue_messages(self, queue, project, pipe):
        """Method to remove all the messages belonging to a queue.

        Will be referenced from the QueueController.
        The pipe to execute deletion will be passed from the QueueController
        executing the operation.
        """
        container = utils._message_container(queue, project)
        remaining = True
        key = ''
        while remaining:
            headers, objects = self._client.get_container(container,
                                                          limit=1000,
                                                          marker=key)
            if not objects:
                return
            remaining = len(objects) == 1000
            key = objects[-1]['name']
            for o in objects:
                try:
                    self._client.delete_object(container, o['name'])
                except swiftclient.ClientException as exc:
                    if exc.http_status == 404:
                        continue
                    raise
Esempio n. 6
0
    def delete(self, queue, claim_id, project=None):
        message_ctrl = self.driver.message_controller
        try:
            header, obj = self._client.get_object(
                utils._claim_container(queue, project),
                claim_id)
            for msg_id in jsonutils.loads(obj):
                try:
                    headers, msg = message_ctrl._find_message(queue, msg_id,
                                                              project)
                except errors.MessageDoesNotExist:
                    continue
                md5 = hashlib.md5()
                md5.update(msg.encode('utf-8'))
                md5 = md5.hexdigest()
                msg = jsonutils.loads(msg)
                content = jsonutils.dumps(
                    {'body': msg['body'], 'claim_id': None, 'ttl': msg['ttl']})
                client_id = headers['x-object-meta-clientid']
                self._client.put_object(
                    utils._message_container(queue, project),
                    msg_id,
                    content,
                    content_type='application/json',
                    headers={'x-object-meta-clientid': client_id,
                             'if-match': md5,
                             'x-delete-at': headers['x-delete-at']})

            self._client.delete_object(
                utils._claim_container(queue, project),
                claim_id)
        except swiftclient.ClientException as exc:
            if exc.http_status != 404:
                raise
Esempio n. 7
0
    def _create_msg(self, queue, msg, client_uuid, project):
        slug = str(uuid.uuid1())
        now = timeutils.utcnow_ts()
        message = {
            'body': msg.get('body', {}),
            'claim_id': None,
            'ttl': msg['ttl'],
            'claim_count': 0,
            'delay_expires': now + msg.get('delay', 0)
        }

        if self.driver.conf.enable_checksum:
            message['checksum'] = s_utils.get_checksum(msg.get('body', None))

        contents = jsonutils.dumps(message)
        utils._put_or_create_container(self._client,
                                       utils._message_container(
                                           queue, project),
                                       slug,
                                       contents=contents,
                                       content_type='application/json',
                                       headers={
                                           'x-object-meta-clientid':
                                           str(client_uuid),
                                           'x-delete-after':
                                           msg['ttl']
                                       })
        return slug
Esempio n. 8
0
    def _list(self, queue, project=None, marker=None,
              limit=storage.DEFAULT_MESSAGES_PER_PAGE,
              echo=False, client_uuid=None,
              include_claimed=False, sort=1):
        """List messages in the queue, oldest first(ish)

        Time ordering and message inclusion in lists are soft, there is no
        global order and times are based on the UTC time of the zaqar-api
        server that the message was created from.

        Here be consistency dragons.
        """
        if not self._queue_ctrl.exists(queue, project):
            raise errors.QueueDoesNotExist(queue, project)

        client = self._client
        container = utils._message_container(queue, project)
        query_string = None
        if sort == -1:
            query_string = 'reverse=on'

        try:
            _, objects = client.get_container(
                container,
                marker=marker,
                # list 2x the objects because some listing items may have
                # expired
                limit=limit * 2,
                query_string=query_string)
        except swiftclient.ClientException as exc:
            if exc.http_status == 404:
                raise errors.QueueDoesNotExist(queue, project)
            raise

        def is_claimed(msg, headers):
            if include_claimed or msg['claim_id'] is None:
                return False
            claim_obj = self.driver.claim_controller._get(
                queue, msg['claim_id'], project)
            return claim_obj is not None and claim_obj['ttl'] > 0

        def is_echo(msg, headers):
            if echo:
                return False
            return headers['x-object-meta-clientid'] == str(client_uuid)

        filters = [
            is_echo,
            is_claimed,
        ]
        marker = {}
        get_object = functools.partial(client.get_object, container)
        list_objects = functools.partial(client.get_container, container,
                                         limit=limit * 2,
                                         query_string=query_string)
        yield utils._filter_messages(objects, filters, marker, get_object,
                                     list_objects, limit=limit)
        yield marker and marker['next']
Esempio n. 9
0
    def _find_message(self, queue, message_id, project):
        try:
            return self._client.get_object(
                utils._message_container(queue, project), message_id)

        except swiftclient.ClientException as exc:
            if exc.http_status == 404:
                raise errors.MessageDoesNotExist(message_id, queue, project)
            else:
                raise
Esempio n. 10
0
    def _find_message(self, queue, message_id, project):
        try:
            return self._client.get_object(
                utils._message_container(queue, project), message_id)

        except swiftclient.ClientException as exc:
            if exc.http_status == 404:
                raise errors.MessageDoesNotExist(message_id, queue, project)
            else:
                raise
Esempio n. 11
0
    def exists(self, queue, project=None):
        try:
            self._client.head_container(utils._message_container(queue,
                                                                 project))

        except swiftclient.ClientException as exc:
            if exc.http_status == 404:
                return False
            raise
        else:
            return True
Esempio n. 12
0
    def exists(self, topic, project=None):
        try:
            self._client.head_container(utils._message_container(topic,
                                                                 project))

        except swiftclient.ClientException as exc:
            if exc.http_status == 404:
                return False
            raise
        else:
            return True
Esempio n. 13
0
    def stats(self, name, project=None):
        if not self._queue_ctrl.exists(name, project=project):
            raise errors.QueueDoesNotExist(name, project)

        total = 0
        claimed = 0
        container = utils._message_container(name, project)

        try:
            _, objects = self._client.get_container(container)
        except swiftclient.ClientException as exc:
            if exc.http_status == 404:
                raise errors.QueueIsEmpty(name, project)

        newest = None
        oldest = None
        now = timeutils.utcnow_ts(True)
        for obj in objects:
            try:
                headers = self._client.head_object(container, obj['name'])
            except swiftclient.ClientException as exc:
                if exc.http_status != 404:
                    raise
            else:
                created = float(headers['x-timestamp'])
                created_iso = datetime.datetime.utcfromtimestamp(
                    created).strftime('%Y-%m-%dT%H:%M:%SZ')
                newest = {
                    'id': obj['name'],
                    'age': now - created,
                    'created': created_iso
                }
                if oldest is None:
                    oldest = copy.deepcopy(newest)
                total += 1
                if headers.get('x-object-meta-claimid'):
                    claimed += 1

        msg_stats = {
            'claimed': claimed,
            'free': total - claimed,
            'total': total,
        }
        if newest is not None:
            msg_stats['newest'] = newest
            msg_stats['oldest'] = oldest

        return {'messages': msg_stats}
Esempio n. 14
0
    def stats(self, name, project=None):
        if not self._queue_ctrl.exists(name, project=project):
            raise errors.QueueDoesNotExist(name, project)

        total = 0
        claimed = 0
        container = utils._message_container(name, project)

        try:
            _, objects = self._client.get_container(container)
        except swiftclient.ClientException as exc:
            if exc.http_status == 404:
                raise errors.QueueIsEmpty(name, project)

        newest = None
        oldest = None
        now = timeutils.utcnow_ts(True)
        for obj in objects:
            try:
                headers = self._client.head_object(container, obj['name'])
            except swiftclient.ClientException as exc:
                if exc.http_status != 404:
                    raise
            else:
                created = float(headers['x-timestamp'])
                created_iso = datetime.datetime.utcfromtimestamp(
                    created).strftime('%Y-%m-%dT%H:%M:%SZ')
                newest = {
                    'id': obj['name'],
                    'age': now - created,
                    'created': created_iso}
                if oldest is None:
                    oldest = copy.deepcopy(newest)
                total += 1
                if headers.get('x-object-meta-claimid'):
                    claimed += 1

        msg_stats = {
            'claimed': claimed,
            'free': total - claimed,
            'total': total,
        }
        if newest is not None:
            msg_stats['newest'] = newest
            msg_stats['oldest'] = oldest

        return {'messages': msg_stats}
Esempio n. 15
0
 def delete(self, name, project=None):
     for container in [utils._message_container(name, project)]:
         try:
             headers, objects = self._client.get_container(container)
         except swiftclient.ClientException as exc:
             if exc.http_status != 404:
                 raise
         else:
             for obj in objects:
                 try:
                     self._client.delete_object(container, obj['name'])
                 except swiftclient.ClientException as exc:
                     if exc.http_status != 404:
                         raise
             try:
                 self._client.delete_container(container)
             except swiftclient.ClientException as exc:
                 if exc.http_status not in (404, 409):
                     raise
Esempio n. 16
0
    def _create_msg(self, queue, msg, client_uuid, project):
        slug = str(uuid.uuid1())
        now = timeutils.utcnow_ts()
        message = {'body': msg.get('body', {}), 'claim_id': None,
                   'ttl': msg['ttl'], 'claim_count': 0,
                   'delay_expires': now + msg.get('delay', 0)}

        if self.driver.conf.enable_checksum:
            message['checksum'] = s_utils.get_checksum(msg.get('body', None))

        contents = jsonutils.dumps(message)
        utils._put_or_create_container(
            self._client,
            utils._message_container(queue, project),
            slug,
            contents=contents,
            content_type='application/json',
            headers={
                'x-object-meta-clientid': str(client_uuid),
                'x-delete-after': msg['ttl']})
        return slug
Esempio n. 17
0
    def delete(self, queue, claim_id, project=None):
        message_ctrl = self.driver.message_controller
        try:
            header, obj = self._client.get_object(
                utils._claim_container(queue, project), claim_id)
            for msg_id in jsonutils.loads(obj):
                try:
                    headers, msg = message_ctrl._find_message(
                        queue, msg_id, project)
                except errors.MessageDoesNotExist:
                    continue
                md5 = hashlib.md5()
                md5.update(msg.encode('utf-8'))
                md5 = md5.hexdigest()
                msg = jsonutils.loads(msg)
                content = jsonutils.dumps({
                    'body': msg['body'],
                    'claim_id': None,
                    'ttl': msg['ttl']
                })
                client_id = headers['x-object-meta-clientid']
                self._client.put_object(
                    utils._message_container(queue, project),
                    msg_id,
                    content,
                    content_type='application/json',
                    headers={
                        'x-object-meta-clientid': client_id,
                        'if-match': md5,
                        'x-delete-at': headers['x-delete-at']
                    })

            self._client.delete_object(utils._claim_container(queue, project),
                                       claim_id)
        except swiftclient.ClientException as exc:
            if exc.http_status != 404:
                raise
Esempio n. 18
0
 def create(self, name, metadata=None, project=None):
     self._client.put_container(utils._message_container(name, project))
Esempio n. 19
0
 def create(self, name, metadata=None, project=None):
     self._client.put_container(utils._message_container(name, project))
Esempio n. 20
0
    def create(self, queue, metadata, project=None,
               limit=storage.DEFAULT_MESSAGES_PER_CLAIM):
        message_ctrl = self.driver.message_controller
        queue_ctrl = self.driver.queue_controller
        try:
            queue_meta = queue_ctrl.get_metadata(queue, project=project)
        except errors.QueueDoesNotExist:
            return None, iter([])
        ttl = metadata['ttl']
        grace = metadata['grace']
        msg_ts = ttl + grace
        claim_id = uuidutils.generate_uuid()

        dlq = True if ('_max_claim_count' in queue_meta and
                       '_dead_letter_queue' in queue_meta) else False

        include_delayed = False if queue_meta.get('_default_message_delay',
                                                  0) else True

        messages, marker = message_ctrl._list(queue, project, limit=limit,
                                              include_claimed=False,
                                              include_delayed=include_delayed)

        claimed = []
        for msg in messages:
            claim_count = msg.get('claim_count', 0)
            md5 = hashlib.md5()
            md5.update(
                jsonutils.dumps(
                    {'body': msg['body'], 'claim_id': None,
                     'ttl': msg['ttl'],
                     'claim_count': claim_count}).encode('utf-8'))
            md5 = md5.hexdigest()
            msg_ttl = max(msg['ttl'], msg_ts)
            move_to_dlq = False
            if dlq:
                if claim_count < queue_meta['_max_claim_count']:
                    # Check if the message's claim count has exceeded the
                    # max claim count defined in the queue, if not ,
                    # Save the new max claim count for message
                    claim_count = claim_count + 1
                else:
                    # if the message's claim count has exceeded the
                    # max claim count defined in the queue, move the
                    # message to the dead letter queue.
                    # NOTE: We're moving message by changing the
                    # project info directly. That means, the queue and dead
                    # letter queue must be created on the same pool.
                    dlq_ttl = queue_meta.get("_dead_letter_queue_messages_ttl")
                    move_to_dlq = True
                    if dlq_ttl:
                        msg_ttl = dlq_ttl

            content = jsonutils.dumps(
                {'body': msg['body'], 'claim_id': claim_id,
                 'ttl': msg_ttl,
                 'claim_count': claim_count})

            if move_to_dlq:
                dead_letter_queue = queue_meta.get("_dead_letter_queue")
                utils._put_or_create_container(
                    self._client,
                    utils._message_container(dead_letter_queue, project),
                    msg['id'],
                    content,
                    content_type='application/json',
                    headers={'x-object-meta-clientid': msg['client_uuid'],
                             'if-match': md5,
                             'x-object-meta-claimid': claim_id,
                             'x-delete-after': msg_ttl})

                message_ctrl._delete(queue, msg['id'], project)

            else:
                try:
                    self._client.put_object(
                        utils._message_container(queue, project),
                        msg['id'],
                        content,
                        content_type='application/json',
                        headers={'x-object-meta-clientid': msg['client_uuid'],
                                 'if-match': md5,
                                 'x-object-meta-claimid': claim_id,
                                 'x-delete-after': msg_ttl})
                except swiftclient.ClientException as exc:
                    if exc.http_status == 412:
                        continue
                    raise
                else:
                    msg['claim_id'] = claim_id
                    msg['ttl'] = msg_ttl
                    msg['claim_count'] = claim_count
                    claimed.append(msg)

        utils._put_or_create_container(
            self._client,
            utils._claim_container(queue, project),
            claim_id,
            jsonutils.dumps([msg['id'] for msg in claimed]),
            content_type='application/json',
            headers={'x-delete-after': ttl}
        )

        return claim_id, claimed
Esempio n. 21
0
    def _list(self, queue, project=None, marker=None,
              limit=storage.DEFAULT_MESSAGES_PER_PAGE,
              echo=False, client_uuid=None,
              include_claimed=False, include_delayed=False,
              sort=1):
        """List messages in the queue, oldest first(ish)

        Time ordering and message inclusion in lists are soft, there is no
        global order and times are based on the UTC time of the zaqar-api
        server that the message was created from.

        Here be consistency dragons.
        """
        if not self._queue_ctrl.exists(queue, project):
            raise errors.QueueDoesNotExist(queue, project)

        client = self._client
        container = utils._message_container(queue, project)
        query_string = None
        if sort == -1:
            query_string = 'reverse=on'

        try:
            _, objects = client.get_container(
                container,
                marker=marker,
                # list 2x the objects because some listing items may have
                # expired
                limit=limit * 2,
                query_string=query_string)
        except swiftclient.ClientException as exc:
            if exc.http_status == 404:
                raise errors.QueueDoesNotExist(queue, project)
            raise

        def is_claimed(msg, headers):
            if include_claimed or msg['claim_id'] is None:
                return False
            claim_obj = self.driver.claim_controller._get(
                queue, msg['claim_id'], project)
            return claim_obj is not None and claim_obj['ttl'] > 0

        def is_delayed(msg, headers):
            if include_delayed:
                return False
            now = timeutils.utcnow_ts()
            return msg.get('delay_expires', 0) > now

        def is_echo(msg, headers):
            if echo:
                return False
            return headers['x-object-meta-clientid'] == str(client_uuid)

        filters = [
            is_echo,
            is_claimed,
            is_delayed,
        ]
        marker = {}
        get_object = functools.partial(client.get_object, container)
        list_objects = functools.partial(client.get_container, container,
                                         limit=limit * 2,
                                         query_string=query_string)
        yield utils._filter_messages(objects, filters, marker, get_object,
                                     list_objects, limit=limit)
        yield marker and marker['next']
Esempio n. 22
0
    def create(self,
               queue,
               metadata,
               project=None,
               limit=storage.DEFAULT_MESSAGES_PER_CLAIM):
        ttl = metadata['ttl']
        grace = metadata['grace']
        msg_ts = ttl + grace
        claim_id = uuidutils.generate_uuid()

        messages, marker = self._message_ctrl._list(queue,
                                                    project,
                                                    limit=limit,
                                                    include_claimed=False)

        claimed = []
        for msg in messages:
            md5 = hashlib.md5()
            md5.update(
                jsonutils.dumps({
                    'body': msg['body'],
                    'claim_id': None,
                    'ttl': msg['ttl']
                }))
            md5 = md5.hexdigest()
            msg_ttl = max(msg['ttl'], msg_ts)
            content = jsonutils.dumps({
                'body': msg['body'],
                'claim_id': claim_id,
                'ttl': msg_ttl
            })
            try:
                self._client.put_object(utils._message_container(
                    queue, project),
                                        msg['id'],
                                        content,
                                        content_type='application/json',
                                        headers={
                                            'x-object-meta-clientid':
                                            msg['client_uuid'],
                                            'if-match':
                                            md5,
                                            'x-object-meta-claimid':
                                            claim_id,
                                            'x-delete-after':
                                            msg_ttl
                                        })
            except swiftclient.ClientException as exc:
                if exc.http_status == 412:
                    continue
                raise
            else:
                msg['claim_id'] = claim_id
                msg['ttl'] = msg_ttl
                claimed.append(msg)

        utils._put_or_create_container(self._client,
                                       utils._claim_container(queue, project),
                                       claim_id,
                                       jsonutils.dumps(
                                           [msg['id'] for msg in claimed]),
                                       content_type='application/json',
                                       headers={'x-delete-after': ttl})

        return claim_id, claimed
Esempio n. 23
0
    def create(self,
               queue,
               metadata,
               project=None,
               limit=storage.DEFAULT_MESSAGES_PER_CLAIM):
        message_ctrl = self.driver.message_controller
        queue_ctrl = self.driver.queue_controller
        try:
            queue_meta = queue_ctrl.get_metadata(queue, project=project)
        except errors.QueueDoesNotExist:
            return None, iter([])
        ttl = metadata['ttl']
        grace = metadata['grace']
        msg_ts = ttl + grace
        claim_id = uuidutils.generate_uuid()

        dlq = True if ('_max_claim_count' in queue_meta
                       and '_dead_letter_queue' in queue_meta) else False

        include_delayed = False if queue_meta.get('_default_message_delay',
                                                  0) else True

        messages, marker = message_ctrl._list(queue,
                                              project,
                                              limit=limit,
                                              include_claimed=False,
                                              include_delayed=include_delayed)

        claimed = []
        for msg in messages:
            claim_count = msg.get('claim_count', 0)
            md5 = hashlib.md5()
            md5.update(
                jsonutils.dumps({
                    'body': msg['body'],
                    'claim_id': None,
                    'ttl': msg['ttl'],
                    'claim_count': claim_count
                }).encode('utf-8'))
            md5 = md5.hexdigest()
            msg_ttl = max(msg['ttl'], msg_ts)
            move_to_dlq = False
            if dlq:
                if claim_count < queue_meta['_max_claim_count']:
                    # Check if the message's claim count has exceeded the
                    # max claim count defined in the queue, if not ,
                    # Save the new max claim count for message
                    claim_count = claim_count + 1
                else:
                    # if the message's claim count has exceeded the
                    # max claim count defined in the queue, move the
                    # message to the dead letter queue.
                    # NOTE: We're moving message by changing the
                    # project info directly. That means, the queue and dead
                    # letter queue must be created on the same pool.
                    dlq_ttl = queue_meta.get("_dead_letter_queue_messages_ttl")
                    move_to_dlq = True
                    if dlq_ttl:
                        msg_ttl = dlq_ttl

            content = jsonutils.dumps({
                'body': msg['body'],
                'claim_id': claim_id,
                'ttl': msg_ttl,
                'claim_count': claim_count
            })

            if move_to_dlq:
                dead_letter_queue = queue_meta.get("_dead_letter_queue")
                utils._put_or_create_container(self._client,
                                               utils._message_container(
                                                   dead_letter_queue, project),
                                               msg['id'],
                                               content,
                                               content_type='application/json',
                                               headers={
                                                   'x-object-meta-clientid':
                                                   msg['client_uuid'],
                                                   'if-match':
                                                   md5,
                                                   'x-object-meta-claimid':
                                                   claim_id,
                                                   'x-delete-after':
                                                   msg_ttl
                                               })

                message_ctrl._delete(queue, msg['id'], project)

            else:
                try:
                    self._client.put_object(utils._message_container(
                        queue, project),
                                            msg['id'],
                                            content,
                                            content_type='application/json',
                                            headers={
                                                'x-object-meta-clientid':
                                                msg['client_uuid'],
                                                'if-match':
                                                md5,
                                                'x-object-meta-claimid':
                                                claim_id,
                                                'x-delete-after':
                                                msg_ttl
                                            })
                except swiftclient.ClientException as exc:
                    if exc.http_status == 412:
                        continue
                    raise
                else:
                    msg['claim_id'] = claim_id
                    msg['ttl'] = msg_ttl
                    msg['claim_count'] = claim_count
                    claimed.append(msg)

        utils._put_or_create_container(self._client,
                                       utils._claim_container(queue, project),
                                       claim_id,
                                       jsonutils.dumps(
                                           [msg['id'] for msg in claimed]),
                                       content_type='application/json',
                                       headers={'x-delete-after': ttl})

        return claim_id, claimed