Esempio n. 1
0
    def _insert(self, project, queue, pool):
        queue_key = utils.scope_queue_name(queue, project)
        catalogue_project_key = utils.scope_pool_catalogue(project,
                                                           CATALOGUE_SUFFIX)
        catalogue_queue_key = utils.scope_pool_catalogue(queue_key,
                                                         CATALOGUE_SUFFIX)
        # Check if the queue already exists.
        if self._exists(queue, project):
            return False

        catalogue = {
            'p': project,
            'p_q': queue,
            'p_p': pool
        }
        # Pipeline ensures atomic inserts.
        with self._client.pipeline() as pipe:
            pipe.zadd(catalogue_project_key, {queue_key: 1})
            pipe.hmset(catalogue_queue_key, catalogue)

            try:
                pipe.execute()
            except redis.exceptions.ResponseError:
                msgtmpl = _(u'CatalogueController:insert %(prj)s:'
                            '%(queue)s  %(pool)s failed')
                LOG.exception(msgtmpl,
                              {'prj': project, 'queue': queue, 'pool': pool})
                return False
        msgtmpl = _(u'CatalogueController:insert %(prj)s:%(queue)s'
                    ':%(pool)s, success')
        LOG.info(msgtmpl,
                 {'prj': project, 'queue': queue, 'pool': pool})
        return True
Esempio n. 2
0
    def delete(self, project, queue):
        # (gengchc): Check if the queue already exists.
        if not self._exists(project, queue):
            return True

        queue_key = utils.scope_queue_name(queue, project)
        catalogue_project_key = utils.scope_pool_catalogue(project,
                                                           CATALOGUE_SUFFIX)
        catalogue_queue_key = utils.scope_pool_catalogue(queue_key,
                                                         CATALOGUE_SUFFIX)
        # (gengchc) Pipeline ensures atomic inserts.
        with self._client.pipeline() as pipe:
            pipe.zrem(catalogue_project_key, queue_key)
            pipe.delete(catalogue_queue_key)
            try:
                pipe.execute()
            except redis.exceptions.ResponseError:
                msgtmpl = _(u'CatalogueController:delete %(prj)s'
                            ':%(queue)s failed')
                LOG.info(msgtmpl,
                         {'prj': project, 'queue': queue})
                return False
        msgtmpl = _(u'CatalogueController:delete %(prj)s:%(queue)s success')
        LOG.info(msgtmpl,
                 {'prj': project, 'queue': queue})
Esempio n. 3
0
    def on_patch(self, req, resp, project_id, queue_name, subscription_id):
        LOG.debug(u'Subscription PATCH - subscription id: %(subscription_id)s,'
                  u' project: %(project)s, queue: %(queue)s',
                  {'subscription_id': subscription_id, 'project': project_id,
                   'queue': queue_name})

        if req.content_length:
            document = wsgi_utils.deserialize(req.stream, req.content_length)
        else:
            document = {}

        try:
            self._validate.subscription_patching(document)
            self._subscription_controller.update(queue_name, subscription_id,
                                                 project=project_id,
                                                 **document)
            resp.status = falcon.HTTP_204
            resp.location = req.path
        except storage_errors.SubscriptionDoesNotExist as ex:
            LOG.debug(ex)
            raise falcon.HTTPNotFound()
        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))
        except Exception as ex:
            LOG.exception(ex)
            description = (_(u'Subscription %(subscription_id)s could not be'
                             ' updated.') %
                           dict(subscription_id=subscription_id))
            raise falcon.HTTPBadRequest(_('Unable to update subscription'),
                                        description)
Esempio n. 4
0
def require_content_type_be_non_urlencoded(req, resp, params):
    """Raises an exception on "x-www-form-urlencoded" content type of request.

    If request has body and "Content-Type" header has
    "application/x-www-form-urlencoded" value (case-insensitive), this function
    raises falcon.HTTPBadRequest exception.

    This strange function exists only to prevent bug/1547100 in a backward
    compatible way.

    Meant to be used as a `before` hook.

    :param req: request sent
    :type req: falcon.request.Request
    :param resp: response object to return
    :type resp: falcon.response.Response
    :param params: additional parameters passed to responders
    :type params: dict
    :rtype: None
    :raises: falcon.HTTPBadRequest

    """
    if req.content_length is None:
        return
    if req.content_type and (req.content_type.lower() ==
                             'application/x-www-form-urlencoded'):
        title = _(u'Invalid Content-Type')
        description = _(u'Endpoint does not accept '
                        u'`application/x-www-form-urlencoded` content; '
                        u'currently supported media type is '
                        u'`application/json`; specify proper client-side '
                        u'media type with the "Content-Type" header.')
        raise falcon.HTTPBadRequest(title, description)
Esempio n. 5
0
    def on_put(self, req, resp, project_id, queue_name, subscription_id):
        if req.content_length:
            document = wsgi_utils.deserialize(req.stream, req.content_length)
        else:
            document = {}

        try:
            self._validate.subscription_confirming(document)
            confirm = document.get('confirmed', None)
            self._subscription_controller.confirm(queue_name, subscription_id,
                                                  project=project_id,
                                                  confirm=confirm)
            resp.status = falcon.HTTP_204
            resp.location = req.path
        except storage_errors.SubscriptionDoesNotExist as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPNotFound(six.text_type(ex))
        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))
        except Exception as ex:
            LOG.exception(ex)
            description = (_(u'Subscription %(subscription_id)s could not be'
                             ' confirmed.') %
                           dict(subscription_id=subscription_id))
            raise falcon.HTTPBadRequest(_('Unable to confirm subscription'),
                                        description)
Esempio n. 6
0
    def on_put(self, request, response, project_id, flavor):
        """Registers a new flavor. Expects the following input:

        ::

            {"pool": "my-pool", "capabilities": {}}

        A capabilities object may also be provided.

        :returns: HTTP | [201, 400]
        """

        LOG.debug(u'PUT flavor - name: %s', flavor)

        data = wsgi_utils.load(request)
        wsgi_utils.validate(self._validators['create'], data)

        try:
            self._ctrl.create(flavor,
                              pool=data['pool'],
                              project=project_id,
                              capabilities=data['capabilities'])
            response.status = falcon.HTTP_201
            response.location = request.path
        except errors.PoolDoesNotExist as ex:
            LOG.exception(ex)
            description = (_(u'Flavor {flavor} could not be created. '
                             u'Pool {pool} does not exist') %
                           dict(flavor=flavor, pool=data['pool']))
            raise falcon.HTTPBadRequest(_('Unable to create'), description)
Esempio n. 7
0
    def claim_creation(self, metadata, limit=None):
        """Restrictions on the claim parameters upon creation.

        :param metadata: The claim metadata
        :param limit: The number of messages to claim
        :raises: ValidationFailed if either TTL or grace is out of range,
            or the expected number of messages exceed the limit.
        """

        self.claim_updating(metadata)

        uplimit = self._limits_conf.max_messages_per_claim_or_pop
        if limit is not None and not (0 < limit <= uplimit):
            msg = _(u'Limit must be at least 1 and may not '
                    'be greater than {0}.')

            raise ValidationFailed(
                msg, self._limits_conf.max_messages_per_claim_or_pop)

        grace = metadata['grace']

        if not (MIN_CLAIM_GRACE <= grace <= self._limits_conf.max_claim_grace):
            msg = _(u'The grace for a claim may not exceed {0} seconds, and '
                    'must be at least {1} seconds long.')

            raise ValidationFailed(
                msg, self._limits_conf.max_claim_grace, MIN_CLAIM_GRACE)
Esempio n. 8
0
    def queue_patching(self, request, changes):
        washed_changes = []
        content_types = {
            'application/openstack-messaging-v2.0-json-patch': 10,
        }

        json_schema_version = content_types[request.content_type]

        if not isinstance(changes, list):
            msg = _('Request body must be a JSON array of operation objects.')
            raise ValidationFailed(msg)

        for raw_change in changes:
            if not isinstance(raw_change, dict):
                msg = _('Operations must be JSON objects.')
                raise ValidationFailed(msg)

            (op, path) = self._parse_json_schema_change(raw_change,
                                                        json_schema_version)

            # NOTE(flwang): Now the 'path' is a list.
            self._validate_path(op, path)
            change = {'op': op, 'path': path,
                      'json_schema_version': json_schema_version}

            if not op == 'remove':
                change['value'] = self._get_change_value(raw_change, op)

            self._validate_change(change)

            washed_changes.append(change)

        return washed_changes
Esempio n. 9
0
    def __init__(self, conf, cache):
        super(DataDriver, self).__init__(conf, cache)

        self.mongodb_conf = self.conf[options.MONGODB_GROUP]

        conn = self.connection
        server_version = conn.server_info()['version']

        if tuple(map(int, server_version.split('.'))) < (2, 2):
            raise RuntimeError(_('The mongodb driver requires mongodb>=2.2,  '
                                 '%s found') % server_version)

        if not len(conn.nodes) > 1 and not conn.is_mongos:
            if not self.conf.unreliable:
                raise RuntimeError(_('Either a replica set or a mongos is '
                                     'required to guarantee message delivery'))
        else:
            wc = conn.write_concern.get('w')
            majority = (wc == 'majority' or
                        wc >= 2)

            if not wc:
                # NOTE(flaper87): No write concern specified, use majority
                # and don't count journal as a replica. Use `update` to avoid
                # overwriting `wtimeout`
                conn.write_concern.update({'w': 'majority'})
            elif not self.conf.unreliable and not majority:
                raise RuntimeError(_('Using a write concern other than '
                                     '`majority` or > 2 makes the service '
                                     'unreliable. Please use a different '
                                     'write concern or set `unreliable` '
                                     'to True in the config file.'))

            conn.write_concern['j'] = False
Esempio n. 10
0
    def subscription_delete(self, req):
        """Delete a specific subscription by ID.

        :param req: Request instance ready to be sent.
        :type req: `api.common.Request`
        :return: resp: Response instance
        :type: resp: `api.common.Response`
        """
        project_id = req._headers.get('X-Project-ID')
        queue_name = req._body.get('queue_name')
        subscription_id = req._body.get('subscription_id')

        LOG.debug(
            u'Subscription delete - queue: %(queue)s, project: %(project)s',
            {'queue': queue_name, 'project': project_id})
        try:
            self._subscription_controller.delete(queue_name,
                                                 subscription_id,
                                                 project=project_id)
        except storage_errors.ExceptionBase as ex:
            LOG.exception(ex)
            error = _('Subscription %(subscription)s for queue %(queue)s '
                      'could not be deleted.') % {
                'subscription': subscription_id, 'queue': queue_name}
            headers = {'status': 503}
            return api_utils.error_response(req, ex, headers, error)
        else:
            body = _('Subscription %s removed.') % subscription_id
            headers = {'status': 204}
            return response.Response(req, body, headers)
Esempio n. 11
0
def require_client_id(validate, req, resp, params):
    """Makes sure the header `Client-ID` is present in the request

    Use as a before hook.
    :param validate: A validator function that will
        be used to check the format of client id against configured
        limits.
    :param req: request sent
    :type req: falcon.request.Request
    :param resp: response object to return
    :type resp: falcon.response.Response
    :param params: additional parameters passed to responders
    :type params: dict
    :rtype: None
    """

    if req.path.startswith('/v1.1/') or req.path.startswith('/v2/'):
        try:
            validate(req.get_header('Client-ID', required=True))
        except ValueError:
            description = _(u'Malformed hexadecimal UUID.')
            raise falcon.HTTPBadRequest('Wrong UUID value', description)
        except validation.ValidationFailed as ex:
            raise falcon.HTTPBadRequest(six.text_type(ex))
    else:
        # NOTE(wanghao): Since we changed the get_client_uuid to support
        # other format of client id, so need to check the uuid here for
        # v1 API.
        try:
            client_id = req.get_header('Client-ID')
            if client_id or client_id == '':
                uuid.UUID(client_id)
        except ValueError:
            description = _(u'Malformed hexadecimal UUID.')
            raise falcon.HTTPBadRequest('Wrong UUID value', description)
Esempio n. 12
0
    def wrapper(self, *args, **kwargs):
        # TODO(prashanthr_) : Try to reuse this utility. Violates DRY
        # Can pass config parameters into the decorator and create a
        # storage level utility.

        max_attemps = self.driver.redis_conf.max_reconnect_attempts
        sleep_sec = self.driver.redis_conf.reconnect_sleep

        for attempt in range(max_attemps):
            try:
                return func(self, *args, **kwargs)

            except redis.exceptions.ConnectionError:
                # NOTE(kgriffs): redis-py will retry once itself,
                # but if the command cannot be sent the second time after
                # disconnecting and reconnecting, the error is raised
                # and we will catch it here.
                #
                # NOTE(kgriffs): When using a sentinel, if a master fails
                # the initial retry will gracefully fail over to the
                # new master if the sentinel failover delay is low enough;
                # if the delay is too long, then redis-py will get a
                # MasterNotFoundError (a subclass of ConnectionError) on
                # it's retry, which will then just get raised and caught
                # here, in which case we will keep retrying until the
                # sentinel completes the failover and stops raising
                # MasterNotFoundError.

                ex = sys.exc_info()[1]
                LOG.warn(_(u"Caught ConnectionError, retrying the " "call to {0}").format(func))

                time.sleep(sleep_sec * (2 ** attempt))
        else:
            LOG.error(_(u"Caught ConnectionError, maximum attempts " "to {0} exceeded.").format(func))
            raise ex
Esempio n. 13
0
    def on_delete(self, req, resp, project_id, queue_name, message_id):
        LOG.debug(u'Messages item DELETE - message: %(message)s, '
                  u'queue: %(queue)s, project: %(project)s',
                  {'message': message_id,
                   'queue': queue_name,
                   'project': project_id})
        try:
            self.message_controller.delete(
                queue_name,
                message_id=message_id,
                project=project_id,
                claim=req.get_param('claim_id'))

        except storage_errors.NotPermitted as ex:
            LOG.exception(ex)
            title = _(u'Unable to delete')
            description = _(u'This message is claimed; it cannot be '
                            u'deleted without a valid claim_id.')
            raise falcon.HTTPForbidden(title, description)

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Message could not be deleted.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        # Alles guete
        resp.status = falcon.HTTP_204
Esempio n. 14
0
    def subscription_patching(self, subscription):
        """Restrictions on an update of subscription.

        :param subscription: dict of subscription
        :raises: ValidationFailed if the subscription is invalid.
        """

        if not subscription:
            raise ValidationFailed(_(u'No subscription to create.'))

        subscriber = subscription.get('subscriber', None)
        subscriber_type = None

        if subscriber:
            parsed_uri = six.moves.urllib_parse.urlparse(subscriber)
            subscriber_type = parsed_uri.scheme

            if subscriber_type not in self._limits_conf.subscriber_types:
                msg = _(u'The subscriber type of subscription must be '
                        u'supported in the list {0}.')
                raise ValidationFailed(msg, self._limits_conf.subscriber_types)

        options = subscription.get('options', None)
        if options and not isinstance(options, dict):
            msg = _(u'Options must be a dict.')
            raise ValidationFailed(msg)

        ttl = subscription.get('ttl', None)
        if ttl and not isinstance(ttl, int):
            msg = _(u'TTL must be an integer.')
            raise ValidationFailed(msg)
Esempio n. 15
0
    def on_put(self, request, response, project_id, flavor):
        """Registers a new flavor. Expects the following input:

        ::

            {"pool": "my-pool", "capabilities": {}}

        A capabilities object may also be provided.

        :returns: HTTP | [201, 400]
        """

        LOG.debug(u"PUT flavor - name: %s", flavor)

        data = wsgi_utils.load(request)
        wsgi_utils.validate(self._validators["create"], data)

        try:
            self._ctrl.create(flavor, pool=data["pool"], project=project_id)
            response.status = falcon.HTTP_201
            response.location = request.path
        except errors.PoolDoesNotExist as ex:
            LOG.exception(ex)
            description = _(u"Flavor %(flavor)s could not be created. " u"Pool %(pool)s does not exist") % dict(
                flavor=flavor, pool=data["pool"]
            )
            raise falcon.HTTPBadRequest(_("Unable to create"), description)
Esempio n. 16
0
    def queue_create(self, req):
        """Creates a queue

        :param req: Request instance ready to be sent.
        :type req: `api.common.Request`
        :return: resp: Response instance
        :type: resp: `api.common.Response`
        """
        project_id = req._headers.get('X-Project-ID')
        queue_name = req._body.get('queue_name')
        metadata = req._body.get('metadata', {})

        LOG.debug(u'Queue create - queue: %(queue)s, project: %(project)s',
                  {'queue': queue_name,
                   'project': project_id})

        try:
            self._validate.queue_identification(queue_name, project_id)
            self._validate.queue_metadata_length(len(str(metadata)))
            created = self._queue_controller.create(queue_name,
                                                    metadata=metadata,
                                                    project=project_id)
        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            headers = {'status': 400}
            return api_utils.error_response(req, ex, headers)
        except storage_errors.ExceptionBase as ex:
            LOG.exception(ex)
            error = _('Queue %s could not be created.') % queue_name
            headers = {'status': 503}
            return api_utils.error_response(req, ex, headers, error)
        else:
            body = _('Queue %s created.') % queue_name
            headers = {'status': 201} if created else {'status': 204}
            return response.Response(req, body, headers)
Esempio n. 17
0
    def on_delete(self, req, resp, project_id, queue_name, message_id):
        error_title = _(u'Unable to delete')

        try:
            self._message_controller.delete(
                queue_name,
                message_id=message_id,
                project=project_id,
                claim=req.get_param('claim_id'))

        except storage_errors.MessageNotClaimed as ex:
            LOG.debug(ex)
            description = _(u'A claim was specified, but the message '
                            u'is not currently claimed.')
            raise falcon.HTTPBadRequest(error_title, description)

        except storage_errors.ClaimDoesNotExist as ex:
            LOG.debug(ex)
            description = _(u'The specified claim does not exist or '
                            u'has expired.')
            raise falcon.HTTPBadRequest(error_title, description)

        except storage_errors.NotPermitted as ex:
            LOG.debug(ex)
            description = _(u'This message is claimed; it cannot be '
                            u'deleted without a valid claim ID.')
            raise falcon.HTTPForbidden(error_title, description)

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Message could not be deleted.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        # Alles guete
        resp.status = falcon.HTTP_204
Esempio n. 18
0
    def queue_metadata_putting(self, queue_metadata):
        """Checking if the reserved attributes of the queue are valid.

        :param queue_metadata: Queue's metadata.
        :raises: ValidationFailed if any reserved attribute is invalid.
        """
        if not queue_metadata:
            return

        queue_default_ttl = queue_metadata.get('_default_message_ttl', None)
        if queue_default_ttl and not isinstance(queue_default_ttl, int):
            msg = _(u'_default_message_ttl must be integer.')
            raise ValidationFailed(msg)

        if queue_default_ttl:
            if not (MIN_MESSAGE_TTL <= queue_default_ttl <=
                    self._limits_conf.max_message_ttl):
                msg = _(u'_default_message_ttl can not exceed {0} '
                        'seconds, and must be at least {1} seconds long.')
                raise ValidationFailed(
                    msg, self._limits_conf.max_message_ttl, MIN_MESSAGE_TTL)

        queue_max_msg_size = queue_metadata.get('_max_messages_post_size',
                                                None)
        if queue_max_msg_size and not isinstance(queue_max_msg_size, int):
            msg = _(u'_max_messages_post_size must be integer.')
            raise ValidationFailed(msg)

        if queue_max_msg_size:
            if not (0 < queue_max_msg_size <=
                    self._limits_conf.max_messages_post_size):
                raise ValidationFailed(
                    _(u'_max_messages_post_size can not exceed {0}, '
                      ' and must be at least greater than 0.'),
                    self._limits_conf.max_messages_post_size)
Esempio n. 19
0
    def queue_get(self, req):
        """Gets a queue

        :param req: Request instance ready to be sent.
        :type req: `api.common.Request`
        :return: resp: Response instance
        :type: resp: `api.common.Response`
        """
        project_id = req._headers.get('X-Project-ID')
        queue_name = req._body.get('queue_name')

        LOG.debug(u'Queue get - queue: %(queue)s, '
                  u'project: %(project)s',
                  {'queue': queue_name, 'project': project_id})

        try:
            resp_dict = self._queue_controller.get(queue_name,
                                                   project=project_id)
        except storage_errors.DoesNotExist as ex:
            LOG.debug(ex)
            error = _('Queue %s does not exist.') % queue_name
            headers = {'status': 404}
            return api_utils.error_response(req, ex, headers, error)
        except storage_errors.ExceptionBase as ex:
            LOG.exception(ex)
            headers = {'status': 503}
            error = _('Cannot retrieve queue %s.') % queue_name
            return api_utils.error_response(req, ex, headers, error)
        else:
            body = resp_dict
            headers = {'status': 200}
            return response.Response(req, body, headers)
Esempio n. 20
0
def get_checked_field(document, name, value_type, default_value):
    """Validates and retrieves a typed field from a document.

    This function attempts to look up doc[name], and raises
    appropriate HTTP errors if the field is missing or not an
    instance of the given type.

    :param document: dict-like object
    :param name: field name
    :param value_type: expected value type, or '*' to accept any type
    :param default_value: Default value to use if the value is missing,
        or None to make the value required.
    :raises HTTPBadRequest: if the field is missing or not an
        instance of value_type
    :returns: value obtained from doc[name]
    """

    try:
        value = document[name]
    except KeyError:
        if default_value is not None:
            value = default_value
        else:
            description = _(u'Missing "{name}" field.').format(name=name)
            raise errors.HTTPBadRequestBody(description)

    # PERF(kgriffs): We do our own little spec thing because it is way
    # faster than jsonschema.
    if value_type == '*' or isinstance(value, value_type):
        return value

    description = _(u'The value of the "{name}" field must be a {vtype}.')
    description = description.format(name=name, vtype=value_type.__name__)
    raise errors.HTTPBadRequestBody(description)
Esempio n. 21
0
    def queue_delete(self, req):
        """Deletes a queue

        :param req: Request instance ready to be sent.
        :type req: `api.common.Request`
        :return: resp: Response instance
        :type: resp: `api.common.Response`
        """
        project_id = req._headers.get('X-Project-ID')
        queue_name = req._body.get('queue_name')

        LOG.debug(u'Queue delete - queue: %(queue)s, project: %(project)s',
                  {'queue': queue_name, 'project': project_id})
        try:
            self._queue_controller.delete(queue_name, project=project_id)
        except storage_errors.ExceptionBase as ex:
            LOG.exception(ex)
            error = _('Queue %s could not be deleted.') % queue_name
            headers = {'status': 503}
            return api_utils.error_response(req, ex, headers, error)
        else:
            body = _('Queue %s removed.') % queue_name
            headers = {'status': 204}
            resp = response.Response(req, body, headers)
            return resp
Esempio n. 22
0
    def message_length(self, content_length, max_msg_post_size=None):
        """Restrictions on message post length.

        :param content_length: Queue request's length.
        :raises: ValidationFailed if the metadata is oversize.
        """
        if content_length is None:
            return

        if max_msg_post_size:
            try:
                min_max_size = min(max_msg_post_size,
                                   self._limits_conf.max_messages_post_size)
                if content_length > min_max_size:
                    raise ValidationFailed(
                        _(u'Message collection size is too large. The max '
                          'size for current queue is {0}. It is calculated '
                          'by max size = min(max_messages_post_size_config: '
                          '{1}, max_messages_post_size_queue: {2}).'),
                        min_max_size,
                        self._limits_conf.max_messages_post_size,
                        max_msg_post_size)
            except TypeError:
                # NOTE(flwang): If there is a type error when using min(),
                # it only happens in py3.x, it will be skipped and compare
                # the message length with the size defined in config file.
                pass

        if content_length > self._limits_conf.max_messages_post_size:
            raise ValidationFailed(
                _(u'Message collection size is too large. Max size {0}'),
                self._limits_conf.max_messages_post_size)
Esempio n. 23
0
def extract_project_id(req, resp, params):
    """Adds `project_id` to the list of params for all responders

    Meant to be used as a `before` hook.

    :param req: request sent
    :type req: falcon.request.Request
    :param resp: response object to return
    :type resp: falcon.response.Response
    :param params: additional parameters passed to responders
    :type params: dict
    :rtype: None
    """
    api_version_string = req.path.split('/')[1]
    params['project_id'] = req.get_header('X-PROJECT-ID')
    if not api_version_string:
        # NOTE(jaosorior): The versions resource is public and shouldn't need
        # a check for the project-id.
        return
    if params['project_id'] == "":
        raise falcon.HTTPBadRequest('Empty project header not allowed',
                                    _(u'X-PROJECT-ID cannot be an empty '
                                      u'string. Specify the right header '
                                      u'X-PROJECT-ID and retry.'))

    api_version = version.LooseVersion(api_version_string)
    if (not params['project_id'] and api_version >=
            version.LooseVersion('v1.1')):
        raise falcon.HTTPBadRequest('Project-Id Missing',
                                    _(u'The header X-PROJECT-ID was missing'))
Esempio n. 24
0
    def __init__(self, conf, cache, control_driver):
        super(DataDriver, self).__init__(conf, cache, control_driver)

        self.mongodb_conf = self.conf[options.MESSAGE_MONGODB_GROUP]

        conn = self.connection
        server_info = conn.server_info()['version']
        self.server_version = tuple(map(int, server_info.split('.')))

        if self.server_version < (2, 2):
            raise RuntimeError(_('The mongodb driver requires mongodb>=2.2, '
                                 '%s found') % server_info)

        if not len(conn.nodes) > 1 and not conn.is_mongos:
            if not self.conf.unreliable:
                raise RuntimeError(_('Either a replica set or a mongos is '
                                     'required to guarantee message delivery'))
        else:

            _mongo_wc = conn.write_concern.document.get('w')
            durable = (_mongo_wc == 'majority' or
                       _mongo_wc >= 2)

            if not self.conf.unreliable and not durable:
                raise RuntimeError(_('Using a write concern other than '
                                     '`majority` or > 2 makes the service '
                                     'unreliable. Please use a different '
                                     'write concern or set `unreliable` '
                                     'to True in the config file.'))

        # FIXME(flaper87): Make this dynamic
        self._capabilities = self.BASE_CAPABILITIES
Esempio n. 25
0
    def on_post(self, req, resp, project_id, queue_name):
        LOG.debug(
            u"Messages collection POST - queue:  %(queue)s, " u"project: %(project)s",
            {"queue": queue_name, "project": project_id},
        )

        client_uuid = wsgi_helpers.get_client_uuid(req)

        try:
            # Place JSON size restriction before parsing
            self._validate.message_length(req.content_length)
        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))

        # Deserialize and validate the request body
        document = wsgi_utils.deserialize(req.stream, req.content_length)
        messages = wsgi_utils.sanitize(document, MESSAGE_POST_SPEC, doctype=wsgi_utils.JSONArray)

        try:
            self._validate.message_posting(messages)

            message_ids = self._message_controller.post(
                queue_name, messages=messages, project=project_id, client_uuid=client_uuid
            )

        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))

        except storage_errors.DoesNotExist as ex:
            LOG.debug(ex)
            raise falcon.HTTPNotFound()

        except storage_errors.MessageConflict as ex:
            LOG.exception(ex)
            description = _(u"No messages could be enqueued.")
            raise wsgi_errors.HTTPServiceUnavailable(description)

        except Exception as ex:
            LOG.exception(ex)
            description = _(u"Messages could not be enqueued.")
            raise wsgi_errors.HTTPServiceUnavailable(description)

        # Prepare the response
        ids_value = ",".join(message_ids)
        resp.location = req.path + "?ids=" + ids_value

        hrefs = [req.path + "/" + id for id in message_ids]

        # NOTE(kgriffs): As of the Icehouse release, drivers are
        # no longer allowed to enqueue a subset of the messages
        # submitted by the client; it's all or nothing. Therefore,
        # 'partial' is now always False in the v1.0 API, and the
        # field has been removed in v1.1.
        body = {"resources": hrefs, "partial": False}

        resp.body = utils.to_json(body)
        resp.status = falcon.HTTP_201
Esempio n. 26
0
    def on_post(self, req, resp, project_id, queue_name):
        client_uuid = wsgi_helpers.get_client_uuid(req)

        try:
            # Place JSON size restriction before parsing
            self._validate.message_length(req.content_length)
        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))

        # Deserialize and validate the incoming messages
        document = wsgi_utils.deserialize(req.stream, req.content_length)

        if 'messages' not in document:
            description = _(u'No messages were found in the request body.')
            raise wsgi_errors.HTTPBadRequestAPI(description)

        messages = wsgi_utils.sanitize(document['messages'],
                                       self._message_post_spec,
                                       doctype=wsgi_utils.JSONArray)

        try:
            self._validate.message_posting(messages)

            if not self._queue_controller.exists(queue_name, project_id):
                self._queue_controller.create(queue_name, project=project_id)

            message_ids = self._message_controller.post(
                queue_name,
                messages=messages,
                project=project_id,
                client_uuid=client_uuid)

        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))

        except storage_errors.DoesNotExist as ex:
            LOG.debug(ex)
            raise falcon.HTTPNotFound()

        except storage_errors.MessageConflict as ex:
            LOG.exception(ex)
            description = _(u'No messages could be enqueued.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Messages could not be enqueued.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        # Prepare the response
        ids_value = ','.join(message_ids)
        resp.location = req.path + '?ids=' + ids_value

        hrefs = [req.path + '/' + id for id in message_ids]
        body = {'resources': hrefs}
        resp.body = utils.to_json(body)
        resp.status = falcon.HTTP_201
Esempio n. 27
0
    def subscription_create(self, req, subscriber):
        """Create a subscription for a queue.

        :param req: Request instance ready to be sent.
        :type req: `api.common.Request`
        :return: resp: Response instance
        :type: resp: `api.common.Response`
        """
        project_id = req._headers.get('X-Project-ID')
        queue_name = req._body.get('queue_name')
        options = req._body.get('options', {})
        ttl = req._body.get('ttl', self._defaults.subscription_ttl)

        LOG.debug(
            u'Subscription create - queue: %(queue)s, project: %(project)s',
            {'queue': queue_name,
             'project': project_id})

        try:
            url = netutils.urlsplit(subscriber)
            mgr = driver.DriverManager('zaqar.notification.tasks', url.scheme,
                                       invoke_on_load=True)
            req_data = req._env.copy()
            mgr.driver.register(subscriber, options, ttl, project_id, req_data)

            data = {'subscriber': subscriber,
                    'options': options,
                    'ttl': ttl}
            self._validate.subscription_posting(data)
            self._validate.queue_identification(queue_name, project_id)
            if not self._queue_controller.exists(queue_name, project_id):
                self._queue_controller.create(queue_name, project=project_id)
            created = self._subscription_controller.create(queue_name,
                                                           subscriber,
                                                           data['ttl'],
                                                           data['options'],
                                                           project=project_id)
        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            headers = {'status': 400}
            return api_utils.error_response(req, ex, headers)
        except storage_errors.ExceptionBase as ex:
            LOG.exception(ex)
            error = _('Subscription %s could not be created.') % queue_name
            headers = {'status': 503}
            return api_utils.error_response(req, ex, headers, error)
        else:
            if created:
                msg = _('Subscription %s created.') % queue_name
                body = {'subscription_id': str(created), 'message': msg}
                headers = {'status': 201}
            else:
                body = _('Subscription %s not created.') % queue_name
                headers = {'status': 409}
            return response.Response(req, body, headers)
Esempio n. 28
0
 def _get_change_operation_d10(self, raw_change):
     op = raw_change.get('op')
     if op is None:
         msg = (_('Unable to find `op` in JSON Schema change. '
                  'It must be one of the following: %(available)s.') %
                {'available': ', '.join(self._supported_operations)})
         raise ValidationFailed(msg)
     if op not in self._supported_operations:
         msg = (_('Invalid operation: `%(op)s`. '
                  'It must be one of the following: %(available)s.') %
                {'op': op,
                 'available': ', '.join(self._supported_operations)})
         raise ValidationFailed(msg)
     return op
Esempio n. 29
0
    def message_delete(self, req):
        """Delete a message from a queue

        :param req: Request instance ready to be sent.
        :type req: `api.common.Request`
        :return: resp: Response instance
        :type: resp: `api.common.Response`
        """
        project_id = req._headers.get('X-Project-ID')
        queue_name = req._body.get('queue_name')
        message_id = req._body.get('message_id')

        LOG.debug(u'Messages item DELETE - message: %(message)s, '
                  u'queue: %(queue)s, project: %(project)s',
                  {'message': message_id,
                   'queue': queue_name,
                   'project': project_id})

        claim_id = req._body.get('claim_id')

        try:
            self._message_controller.delete(
                queue_name,
                message_id=message_id,
                project=project_id,
                claim=claim_id)
        except storage_errors.MessageNotClaimed as ex:
            LOG.debug(ex)
            error = _(u'A claim was specified, but the message '
                      u'is not currently claimed.')
            headers = {'status': 400}
            return api_utils.error_response(req, ex, headers, error)
        except storage_errors.ClaimDoesNotExist as ex:
            LOG.debug(ex)
            error = _(u'The specified claim does not exist or '
                      u'has expired.')
            headers = {'status': 400}
            return api_utils.error_response(req, ex, headers, error)
        except storage_errors.NotPermitted as ex:
            LOG.debug(ex)
            error = _(u'This message is claimed; it cannot be '
                      u'deleted without a valid claim ID.')
            headers = {'status': 403}
            return api_utils.error_response(req, ex, headers, error)

        headers = {'status': 204}
        body = {}

        return response.Response(req, body, headers)
Esempio n. 30
0
    def subscription_create(self, req, subscriber):
        """Create a subscription for a queue.

        :param req: Request instance ready to be sent.
        :type req: `api.common.Request`
        :return: resp: Response instance
        :type: resp: `api.common.Response`
        """
        project_id = req._headers.get('X-Project-ID')
        queue_name = req._body.get('queue_name')

        LOG.debug(
            u'Subscription create - queue: %(queue)s, project: %(project)s',
            {'queue': queue_name,
             'project': project_id})

        try:
            data = {'subscriber': subscriber,
                    'options': req._body.get('options'),
                    'ttl': req._body.get('ttl')}
            self._validate.subscription_posting(data)
            created = self._subscription_controller.create(queue_name,
                                                           subscriber,
                                                           data['ttl'],
                                                           data['options'],
                                                           project=project_id)
        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            headers = {'status': 400}
            return api_utils.error_response(req, ex, headers)
        except storage_errors.DoesNotExist as ex:
            LOG.debug(ex)
            error = _('Queue %s does not exist.') % queue_name
            headers = {'status': 404}
            return api_utils.error_response(req, ex, headers, error)
        except storage_errors.ExceptionBase as ex:
            LOG.exception(ex)
            error = _('Subscription %s could not be created.') % queue_name
            headers = {'status': 503}
            return api_utils.error_response(req, ex, headers, error)
        else:
            if created:
                msg = _('Subscription %s created.') % queue_name
                body = {'subscription_id': str(created), 'message': msg}
                headers = {'status': 201}
            else:
                body = _('Subscription %s not created.') % queue_name
                headers = {'status': 409}
            return response.Response(req, body, headers)
Esempio n. 31
0
    def on_get(self, req, resp, project_id, queue_name):
        try:
            resp_dict = self._queue_controller.get(queue_name,
                                                   project=project_id)

        except storage_errors.DoesNotExist as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPNotFound(six.text_type(ex))

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Queue metadata could not be retrieved.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        resp.body = utils.to_json(resp_dict)
Esempio n. 32
0
    def _update(self, project, queue, pool):
        # Check if the queue already exists.
        if not self._exists(project, queue):
            raise errors.QueueNotMapped(queue, project)

        queue_key = utils.scope_queue_name(queue, project)
        catalogue_queue_key = utils.scope_pool_catalogue(
            queue_key, CATALOGUE_SUFFIX)
        with self._client.pipeline() as pipe:
            pipe.hset(catalogue_queue_key, "pl", pool)
            try:
                pipe.execute()
            except redis.exceptions.ResponseError:
                msgtmpl = _(u'CatalogueController:_update %(prj)s'
                            ':%(queue)s:%(pool)s failed')
                LOG.exception(msgtmpl, {
                    'prj': project,
                    'queue': queue,
                    'pool': pool
                })
                return False
        msgtmpl = _(u'CatalogueController:_update %(prj)s:%(queue)s'
                    ':%(pool)s')
        LOG.info(msgtmpl, {'prj': project, 'queue': queue, 'pool': pool})
Esempio n. 33
0
    def _delete_messages_by_id(self, queue_name, ids, project_id,
                               claim_ids=None):
        try:
            self._message_controller.bulk_delete(
                queue_name,
                message_ids=ids,
                project=project_id,
                claim_ids=claim_ids)

        except Exception:
            description = _(u'Messages could not be deleted.')
            LOG.exception(description)
            raise wsgi_errors.HTTPServiceUnavailable(description)

        return falcon.HTTP_204
Esempio n. 34
0
    def on_put(self, req, resp, project_id, queue_name):
        LOG.debug(u'Queue item PUT - queue: %(queue)s, ',
                  {'queue': queue_name})

        try:
            created = self._queue_controller.create(queue_name,
                                                    project=project_id)

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Queue could not be created.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        resp.status = falcon.HTTP_201 if created else falcon.HTTP_204
        resp.location = req.path
Esempio n. 35
0
def get_client_uuid(req):
    """Read a required Client-ID from a request.

    :param req: A falcon.Request object
    :raises: HTTPBadRequest if the Client-ID header is missing or
        does not represent a valid UUID
    :returns: A UUID object
    """

    try:
        return uuid.UUID(req.get_header('Client-ID', required=True))

    except ValueError:
        description = _(u'Malformed hexadecimal UUID.')
        raise falcon.HTTPBadRequest('Wrong UUID value', description)
Esempio n. 36
0
    def __init__(self, conf, cache):
        super(DataDriver, self).__init__(conf, cache)

        self.mongodb_conf = self.conf[options.MONGODB_GROUP]

        conn = self.connection
        server_version = conn.server_info()['version']

        if tuple(map(int, server_version.split('.'))) < (2, 2):
            raise RuntimeError(
                _('The mongodb driver requires mongodb>=2.2,  '
                  '%s found') % server_version)

        if not len(conn.nodes) > 1 and not conn.is_mongos:
            if not self.conf.unreliable:
                raise RuntimeError(
                    _('Either a replica set or a mongos is '
                      'required to guarantee message delivery'))
        else:
            wc = conn.write_concern.get('w')
            majority = (wc == 'majority' or wc >= 2)

            if not wc:
                # NOTE(flaper87): No write concern specified, use majority
                # and don't count journal as a replica. Use `update` to avoid
                # overwriting `wtimeout`
                conn.write_concern.update({'w': 'majority'})
            elif not self.conf.unreliable and not majority:
                raise RuntimeError(
                    _('Using a write concern other than '
                      '`majority` or > 2 makes the service '
                      'unreliable. Please use a different '
                      'write concern or set `unreliable` '
                      'to True in the config file.'))

            conn.write_concern['j'] = False
Esempio n. 37
0
    def queue_create(self, req):
        """Creates a queue

        :param req: Request instance ready to be sent.
        :type req: `api.common.Request`
        :return: resp: Response instance
        :type: resp: `api.common.Response`
        """
        project_id = req._headers.get('X-Project-ID')
        queue_name = req._body.get('queue_name')
        metadata = req._body.get('metadata', {})

        LOG.debug(u'Queue create - queue: %(queue)s, project: %(project)s', {
            'queue': queue_name,
            'project': project_id
        })

        try:
            self._validate.queue_identification(queue_name, project_id)
            self._validate.queue_metadata_length(len(str(metadata)))
            created = self._queue_controller.create(queue_name,
                                                    metadata=metadata,
                                                    project=project_id)
        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            headers = {'status': 400}
            return api_utils.error_response(req, ex, headers)
        except storage_errors.ExceptionBase as ex:
            LOG.exception(ex)
            error = _('Queue %s could not be created.') % queue_name
            headers = {'status': 503}
            return api_utils.error_response(req, ex, headers, error)
        else:
            body = _('Queue %s created.') % queue_name
            headers = {'status': 201} if created else {'status': 204}
            return response.Response(req, body, headers)
Esempio n. 38
0
    def __init__(self, conf):
        self.conf = conf
        self.conf.register_opts(_GENERAL_OPTIONS)
        self.conf.register_opts(_DRIVER_OPTIONS, group=_DRIVER_GROUP)
        self.driver_conf = self.conf[_DRIVER_GROUP]

        log.setup('zaqar')

        if self.conf.unreliable is None:
            msg = _(u'Unreliable\'s default value will be changed to False '
                    'in the Kilo release. Please, make sure your deployments '
                    'are working in a reliable mode or that `unreliable` is '
                    'explicitly set to `True` in your configuration files.')
            LOG.warn(msg)
            self.conf.unreliable = True
Esempio n. 39
0
    def message_deletion(self, ids=None, pop=None):
        """Restrictions involving deletion of messages.

        :param ids: message ids passed in by the delete request
        :param pop: count of messages to be POPped
        :raises ValidationFailed: if,
            pop AND id params are present together
            neither pop or id params are present
            message count to be popped > maximum allowed
        """

        if pop is not None and ids is not None:
            msg = _(u'pop and id params cannot be present together in the '
                    'delete request.')

            raise ValidationFailed(msg)

        if pop is None and ids is None:
            msg = _(u'The request should have either "ids" or "pop" '
                    'parameter in the request, to be able to delete.')

            raise ValidationFailed(msg)

        pop_uplimit = self._limits_conf.max_messages_per_claim_or_pop
        if pop is not None and not (0 < pop <= pop_uplimit):
            msg = _(u'Pop value must be at least 1 and may not '
                    'be greater than {0}.')

            raise ValidationFailed(msg, pop_uplimit)

        delete_uplimit = self._limits_conf.max_messages_per_page
        if ids is not None and not (0 < len(ids) <= delete_uplimit):
            msg = _(u'ids parameter should have at least 1 and not '
                    'greater than {0} values.')

            raise ValidationFailed(msg, delete_uplimit)
Esempio n. 40
0
    def claim_updating(self, metadata):
        """Restrictions on the claim TTL.

        :param metadata: The claim metadata
        :raises ValidationFailed: if the TTL is out of range
        """

        ttl = metadata['ttl']

        if not (MIN_CLAIM_TTL <= ttl <= self._limits_conf.max_claim_ttl):
            msg = _(u'The TTL for a claim may not exceed {0} seconds, and '
                    'must be at least {1} seconds long.')

            raise ValidationFailed(
                msg, self._limits_conf.max_claim_ttl, MIN_CLAIM_TTL)
Esempio n. 41
0
def extract_project_id(req, resp, params):
    """Adds `project_id` to the list of params for all responders

    Meant to be used as a `before` hook.

    :param req: request sent
    :type req: falcon.request.Request
    :param resp: response object to return
    :type resp: falcon.response.Response
    :param params: additional parameters passed to responders
    :type params: dict
    :rtype: None
    """
    params['project_id'] = req.get_header('X-PROJECT-ID')
    if params['project_id'] == "":
        raise falcon.HTTPBadRequest('Empty project header not allowed',
                                    _(u'''
X-PROJECT-ID cannot be an empty string. Specify the right header X-PROJECT-ID
and retry.'''))

    # TODO(flaper87): Make version comparison smarter to support v2_0.
    if not params['project_id'] and 'v1.1' in req.path:
        raise falcon.HTTPBadRequest('Project-Id Missing',
                                    _(u'The header X-PROJECT-ID was missing'))
Esempio n. 42
0
    def subscription_listing(self, limit=None, **kwargs):
        """Restrictions involving a list of subscriptions.

        :param limit: The expected number of subscriptions in the list
        :param kwargs: Ignored arguments passed to storage API
        :raises ValidationFailed: if the limit is exceeded
        """

        uplimit = self._limits_conf.max_subscriptions_per_page
        if limit is not None and not (0 < limit <= uplimit):
            msg = _(u'Limit must be at least 1 and may not '
                    'be greater than {0}.')

            raise ValidationFailed(
                msg, self._limits_conf.max_subscriptions_per_page)
Esempio n. 43
0
    def queue_delete(self, req):
        """Deletes a queue

        :param req: Request instance ready to be sent.
        :type req: `api.common.Request`
        :return: resp: Response instance
        :type: resp: `api.common.Response`
        """
        project_id = req._headers.get('X-Project-ID')
        queue_name = req._body.get('queue_name')

        LOG.debug(u'Queue delete - queue: %(queue)s, project: %(project)s',
                  {'queue': queue_name, 'project': project_id})
        try:
            self._queue_controller.delete(queue_name, project=project_id)
        except storage_errors.ExceptionBase as ex:
            LOG.exception(ex)
            error = _('Queue %s could not be deleted.') % queue_name
            headers = {'status': 503}
            return api_utils.error_response(req, ex, headers, error)
        else:
            body = _('Queue %s removed.') % queue_name
            headers = {'status': 204}
            return response.Response(req, body, headers)
Esempio n. 44
0
    def on_get(self, req, resp, project_id):
        LOG.debug(u'Queue collection GET')

        kwargs = {}

        # NOTE(kgriffs): This syntax ensures that
        # we don't clobber default values with None.
        req.get_param('marker', store=kwargs)
        req.get_param_as_int('limit', store=kwargs)
        req.get_param_as_bool('detailed', store=kwargs)

        try:
            self._validate.queue_listing(**kwargs)
            results = self._queue_controller.list(project=project_id, **kwargs)

            # Buffer list of queues
            queues = list(next(results))

        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))

        except Exception:
            description = _(u'Queues could not be listed.')
            LOG.exception(description)
            raise wsgi_errors.HTTPServiceUnavailable(description)

        # Check for an empty list
        if len(queues) == 0:
            resp.status = falcon.HTTP_204
            return

        # Got some. Prepare the response.
        kwargs['marker'] = next(results)
        for each_queue in queues:
            each_queue['href'] = req.path + '/' + each_queue['name']

        response_body = {
            'queues':
            queues,
            'links': [{
                'rel': 'next',
                'href': req.path + falcon.to_query_str(kwargs)
            }]
        }

        resp.content_location = req.relative_uri
        resp.body = utils.to_json(response_body)
Esempio n. 45
0
    def on_get(self, req, resp, project_id, queue_name, subscription_id):
        try:
            resp_dict = self._subscription_controller.get(queue_name,
                                                          subscription_id,
                                                          project=project_id)

        except storage_errors.DoesNotExist as ex:
            LOG.debug(ex)
            raise falcon.HTTPNotFound()

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Subscription could not be retrieved.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        resp.body = utils.to_json(resp_dict)
Esempio n. 46
0
    def on_delete(self, req, resp, project_id, queue_name):
        LOG.debug(
            u'Queue item DELETE - queue: %(queue)s, '
            u'project: %(project)s', {
                'queue': queue_name,
                'project': project_id
            })
        try:
            self._queue_controller.delete(queue_name, project=project_id)

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Queue could not be deleted.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        resp.status = falcon.HTTP_204
Esempio n. 47
0
    def on_delete(self, req, resp, project_id, topic_name):
        LOG.debug(
            u'Topic item DELETE - topic: %(topic)s, '
            u'project: %(project)s', {
                'topic': topic_name,
                'project': project_id
            })
        try:
            self._topic_controller.delete(topic_name, project=project_id)

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Topic could not be deleted.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        resp.status = falcon.HTTP_204
Esempio n. 48
0
    def on_get(self, req, resp, project_id, queue_name):
        try:
            resp_dict = self._queue_ctrl.get_metadata(queue_name,
                                                      project=project_id)

        except storage_errors.DoesNotExist as ex:
            LOG.debug(ex)
            raise falcon.HTTPNotFound()

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Queue metadata could not be retrieved.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        resp.content_location = req.path
        resp.body = utils.to_json(resp_dict)
Esempio n. 49
0
    def on_post(self, req, resp, project_id, queue_name):
        LOG.debug(u'Claims collection POST - queue: %(queue)s, '
                  u'project: %(project)s',
                  {'queue': queue_name, 'project': project_id})

        # Check for an explicit limit on the # of messages to claim
        limit = req.get_param_as_int('limit')
        claim_options = {} if limit is None else {'limit': limit}

        # Read claim metadata (e.g., TTL) and raise appropriate
        # HTTP errors as needed.
        document = wsgi_utils.deserialize(req.stream, req.content_length)
        metadata = wsgi_utils.sanitize(document, CLAIM_POST_SPEC)

        # Claim some messages
        try:
            self._validate.claim_creation(metadata, limit=limit)
            cid, msgs = self._claim_controller.create(
                queue_name,
                metadata=metadata,
                project=project_id,
                **claim_options)

            # Buffer claimed messages
            # TODO(kgriffs): optimize, along with serialization (below)
            resp_msgs = list(msgs)

        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Claim could not be created.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        # Serialize claimed messages, if any. This logic assumes
        # the storage driver returned well-formed messages.
        if len(resp_msgs) != 0:
            resp_msgs = [wsgi_utils.format_message_v1(
                msg, req.path.rpartition('/')[0], cid) for msg in resp_msgs]

            resp.location = req.path + '/' + cid
            resp.body = utils.to_json(resp_msgs)
            resp.status = falcon.HTTP_201
        else:
            resp.status = falcon.HTTP_204
Esempio n. 50
0
    def on_delete(self, req, resp, project_id, queue_name, claim_id):
        LOG.debug(u'Claim item DELETE - claim: %(claim_id)s, '
                  u'queue: %(queue_name)s, project: %(project_id)s' %
                  {'queue_name': queue_name,
                   'project_id': project_id,
                   'claim_id': claim_id})
        try:
            self.claim_controller.delete(queue_name,
                                         claim_id=claim_id,
                                         project=project_id)

            resp.status = falcon.HTTP_204

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Claim could not be deleted.')
            raise wsgi_errors.HTTPServiceUnavailable(description)
Esempio n. 51
0
    def client_id_uuid_safe(self, client_id):
        """Restrictions the format of client id

        :param client_id: the client id of request
        :raises ValidationFailed: if the limit is exceeded
        """

        if self._limits_conf.client_id_uuid_safe == 'off':
            if (len(client_id) < self._limits_conf.min_length_client_id) or \
               (len(client_id) > self._limits_conf.max_length_client_id):
                msg = _(u'Length of client id must be at least {0} and no '
                        'greater than {1}.')
                raise ValidationFailed(msg,
                                       self._limits_conf.min_length_client_id,
                                       self._limits_conf.max_length_client_id)
        if self._limits_conf.client_id_uuid_safe == 'strict':
            uuid.UUID(client_id)
Esempio n. 52
0
    def on_get(self, req, resp, project_id, topic_name):
        try:
            resp_dict = self._topic_controller.get(topic_name,
                                                   project=project_id)
            for meta, value in _get_reserved_metadata(self._validate).items():
                if not resp_dict.get(meta):
                    resp_dict[meta] = value
        except storage_errors.DoesNotExist as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPNotFound(six.text_type(ex))

        except Exception:
            description = _(u'Topic metadata could not be retrieved.')
            LOG.exception(description)
            raise wsgi_errors.HTTPServiceUnavailable(description)

        resp.body = utils.to_json(resp_dict)
Esempio n. 53
0
    def on_get(self, req, resp, project_id, queue_name):
        LOG.debug(
            u'Subscription collection GET - project: %(project)s, '
            u'queue: %(queue)s', {
                'project': project_id,
                'queue': queue_name
            })

        kwargs = {}

        # NOTE(kgriffs): This syntax ensures that
        # we don't clobber default values with None.
        req.get_param('marker', store=kwargs)
        req.get_param_as_int('limit', store=kwargs)

        try:
            self._validate.subscription_listing(**kwargs)
            results = self._subscription_controller.list(queue_name,
                                                         project=project_id,
                                                         **kwargs)
        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Subscriptions could not be listed.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        # Buffer list of subscriptions
        subscriptions = list(next(results))

        # Got some. Prepare the response.
        kwargs['marker'] = next(results) or kwargs.get('marker', '')

        response_body = {
            'subscriptions':
            subscriptions,
            'links': [{
                'rel': 'next',
                'href': req.path + falcon.to_query_str(kwargs)
            }]
        }

        resp.body = utils.to_json(response_body)
Esempio n. 54
0
    def on_get(self, req, resp, project_id):
        kwargs = {}

        # NOTE(kgriffs): This syntax ensures that
        # we don't clobber default values with None.
        req.get_param('marker', store=kwargs)
        req.get_param_as_int('limit', store=kwargs)
        req.get_param_as_bool('detailed', store=kwargs)

        try:
            self._validate.queue_listing(**kwargs)
            results = self._queue_controller.list(project=project_id, **kwargs)

            # Buffer list of queues
            queues = list(next(results))

        except validation.ValidationFailed as ex:
            LOG.debug(ex)
            raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Queues could not be listed.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        # Got some. Prepare the response.
        kwargs['marker'] = next(results) or kwargs.get('marker', '')
        reserved_metadata = _get_reserved_metadata(self._validate).items()
        for each_queue in queues:
            each_queue['href'] = req.path + '/' + each_queue['name']
            if kwargs.get('detailed'):
                for meta, value in reserved_metadata:
                    if not each_queue.get('metadata', {}).get(meta):
                        each_queue['metadata'][meta] = value

        links = []
        if queues:
            links = [{
                'rel': 'next',
                'href': req.path + falcon.to_query_str(kwargs)
            }]

        response_body = {'queues': queues, 'links': links}

        resp.body = utils.to_json(response_body)
Esempio n. 55
0
    def claim_get(self, req):
        """Gets a claim

        :param req: Request instance ready to be sent.
        :type req: `api.common.Request`
        :return: resp: Response instance
        :type: resp: `api.common.Response`
        """
        project_id = req._headers.get('X-Project-ID')
        queue_name = req._body.get('queue_name')
        claim_id = req._body.get('claim_id')

        LOG.debug(
            u'Claim get - claim: %(claim_id)s, '
            u'queue: %(queue_name)s, project: %(project_id)s', {
                'queue_name': queue_name,
                'project_id': project_id,
                'claim_id': claim_id
            })
        try:
            meta, msgs = self._claim_controller.get(queue_name,
                                                    claim_id=claim_id,
                                                    project=project_id)

            # Buffer claimed messages
            # TODO(vkmc): Optimize along with serialization (see below)
            meta['messages'] = list(msgs)
        except storage_errors.DoesNotExist as ex:
            LOG.debug(ex)
            error = _('Claim %s does not exist.') % claim_id
            headers = {'status': 404}
            return api_utils.error_response(req, ex, headers, error)

        # Serialize claimed messages
        # TODO(vkmc): Optimize
        meta['messages'] = [
            api_utils.format_message(msg, claim_id) for msg in meta['messages']
        ]

        del meta['id']

        headers = {'status': 200}
        body = meta

        return response.Response(req, body, headers)
Esempio n. 56
0
File: api.py Progetto: xglhjk6/zaqar
    def get_schema(self, action):
        """Returns the schema for an action

        :param action: Action for which params need
            to be validated.
        :type action: `six.text_type`

        :returns: Action's schema
        :rtype: dict

        :raises InvalidAction: if the action does not exist
        """

        try:
            return self.schema[action]
        except KeyError:
            msg = _('{0} is not a valid action').format(action)
            raise errors.InvalidAction(msg)
Esempio n. 57
0
    def subscription_patching(self, subscription):
        """Restrictions on an update of subscription.

        :param subscription: dict of subscription
        :raises: ValidationFailed if the subscription is invalid.
        """

        if not subscription:
            raise ValidationFailed(_(u'No subscription to create.'))

        subscriber = subscription.get('subscriber', None)
        subscriber_type = None

        if subscriber:
            parsed_uri = six.moves.urllib_parse.urlparse(subscriber)
            subscriber_type = parsed_uri.scheme

            if subscriber_type not in self._limits_conf.subscriber_types:
                msg = _(u'The subscriber type of subscription must be '
                        u'supported in the list {0}.')
                raise ValidationFailed(msg, self._limits_conf.subscriber_types)

        options = subscription.get('options', None)
        if options and not isinstance(options, dict):
            msg = _(u'Options must be a dict.')
            raise ValidationFailed(msg)

        ttl = subscription.get('ttl', None)
        if ttl:
            if not isinstance(ttl, int):
                msg = _(u'TTL must be an integer.')
                raise ValidationFailed(msg)

            if ttl < MIN_SUBSCRIPTION_TTL:
                msg = _(u'The TTL for a subscription '
                        'must be at least {0} seconds long.')
                raise ValidationFailed(msg, MIN_SUBSCRIPTION_TTL)

            # NOTE(flwang): By this change, technically, user can set a very
            # big TTL so as to get a very long subscription.
            now = timeutils.utcnow_ts()
            now_dt = datetime.datetime.utcfromtimestamp(now)
            msg = _(u'The TTL seconds for a subscription plus current time'
                    ' must be less than {0}.')
            try:
                # NOTE(flwang): If below expression works, then we believe the
                # ttl is acceptable otherwise it exceeds the max time of
                # python.
                now_dt + datetime.timedelta(seconds=ttl)
            except OverflowError:
                raise ValidationFailed(msg, datetime.datetime.max)
Esempio n. 58
0
def sanitize(document, spec=None, doctype=dict):
    """Validates a document and drops undesired fields.

    :param document: A dict to verify according to `spec`.
    :param spec: (Default None) Iterable describing expected fields,
        yielding tuples with the form of:

            (field_name, value_type, default_value)

        Note that value_type may either be a Python type, or the
        special string '*' to accept any type. default_value is the
        default to give the field if it is missing, or None to require
        that the field be present.

        If spec is None, the incoming documents will not be validated.
    :param doctype: type of document to expect; must be either
        JSONObject or JSONArray.
    :raises DocumentTypeNotSupported: if document type is not supported
    :raises TypeError: if document type is neither a JSONObject
        nor JSONArray
    :returns: A sanitized, filtered version of the document. If the
        document is a list of objects, each object will be filtered
        and returned in a new list. If, on the other hand, the document
        is expected to contain a single object, that object's fields will
        be filtered and the resulting object will be returned.
    """

    if doctype is dict:
        if not isinstance(document, dict):
            raise api_errors.DocumentTypeNotSupported()

        return document if spec is None else filter_fields(document, spec)

    if doctype is list:
        if not isinstance(document, list):
            raise api_errors.DocumentTypeNotSupported()

        if spec is None:
            return document

        return [filter_fields(obj, spec) for obj in document]

    raise TypeError(_(u'Doctype must be either a JSONObject or JSONArray'))
Esempio n. 59
0
class Checks(upgradecheck.UpgradeCommands):

    """Upgrade checks for the zaqar-status upgrade check command

    Upgrade checks should be added as separate methods in this class
    and added to _upgrade_checks tuple.
    """

    # The format of the check functions is to return an
    # oslo_upgradecheck.upgradecheck.Result
    # object with the appropriate
    # oslo_upgradecheck.upgradecheck.Code and details set.
    # If the check hits warnings or failures then those should be stored
    # in the returned Result's "details" attribute. The
    # summary will be rolled up at the end of the check() method.
    _upgrade_checks = (
        (_('policy File JSON to YAML Migration'),
         (common_checks.check_policy_json, {'conf': cfg.CONF})),
    )
Esempio n. 60
0
    def on_get(self, req, resp, project_id, queue_name):
        LOG.debug(u'Queue metadata GET - queue: %(queue)s, '
                  u'project: %(project)s',
                  {'queue': queue_name, 'project': project_id})

        try:
            resp_dict = self._queue_controller.get(queue_name,
                                                   project=project_id)

        except storage_errors.DoesNotExist as ex:
            LOG.debug(ex)
            raise falcon.HTTPNotFound()

        except Exception as ex:
            LOG.exception(ex)
            description = _(u'Queue metadata could not be retrieved.')
            raise wsgi_errors.HTTPServiceUnavailable(description)

        resp.body = utils.to_json(resp_dict)