コード例 #1
0
ファイル: summary.py プロジェクト: noyonict/cloudkitty
class Summary(base.BaseResource):
    """Resource allowing to retrieve a rating summary."""
    @api_utils.paginated
    @api_utils.add_input_schema(
        'query', {
            voluptuous.Optional('groupby'):
            api_utils.MultiQueryParam(str),
            voluptuous.Optional('filters'):
            api_utils.SingleDictQueryParam(str, str),
            voluptuous.Optional('begin'):
            api_utils.SingleQueryParam(tzutils.dt_from_iso),
            voluptuous.Optional('end'):
            api_utils.SingleQueryParam(tzutils.dt_from_iso),
        })
    def get(self,
            groupby=None,
            filters={},
            begin=None,
            end=None,
            offset=0,
            limit=100):
        policy.authorize(flask.request.context, 'summary:get_summary',
                         {'project_id': flask.request.context.project_id})
        begin = begin or tzutils.get_month_start()
        end = end or tzutils.get_next_month()

        if not flask.request.context.is_admin:
            if flask.request.context.project_id is None:
                # Unscoped non-admin user
                return {
                    'total': 0,
                    'columns': [],
                    'results': [],
                }
            filters['project_id'] = flask.request.context.project_id

        metric_types = [filters.pop('type')] if 'type' in filters else None
        total = self._storage.total(
            begin=begin,
            end=end,
            groupby=groupby,
            filters=filters,
            metric_types=metric_types,
            offset=offset,
            limit=limit,
            paginate=True,
        )
        columns = []
        if len(total['results']) > 0:
            columns = list(total['results'][0].keys())

        return {
            'total': total['total'],
            'columns': columns,
            'results': [list(res.values()) for res in total['results']]
        }
コード例 #2
0
 def test_raises_length_invalid_long_list(self):
     validator = api_utils.SingleQueryParam(int)
     self.assertRaises(
         voluptuous.LengthInvalid,
         validator,
         [0, 1],
     )
コード例 #3
0
ファイル: reprocess.py プロジェクト: openstack/cloudkitty
class ReprocessSchedulerGetApi(base.BaseResource):
    def __init__(self, *args, **kwargs):
        super(ReprocessSchedulerGetApi, self).__init__(*args, **kwargs)
        self.schedule_reprocessing_db = storage_state.ReprocessingSchedulerDb()

    @api_utils.paginated
    @api_utils.add_input_schema('query', {
        voluptuous.Optional('scope_ids'): api_utils.MultiQueryParam(str),
        voluptuous.Optional('order', default="desc"):
            api_utils.SingleQueryParam(str)
    })
    @api_utils.add_output_schema({'results': [{
        voluptuous.Required('reason'): validation_utils.get_string_type(),
        voluptuous.Required('scope_id'): validation_utils.get_string_type(),
        voluptuous.Required('start_reprocess_time'):
            validation_utils.get_string_type(),
        voluptuous.Required('end_reprocess_time'):
            validation_utils.get_string_type(),
        voluptuous.Required('current_reprocess_time'):
            validation_utils.get_string_type(),
    }]})
    def get(self, scope_ids=[], path_scope_id=None, offset=0, limit=100,
            order="desc"):
        if path_scope_id and scope_ids:
            LOG.warning("Filtering by scope IDs [%s] and path scope ID [%s] "
                        "does not make sense. You should use only one of "
                        "them. We will use only the path scope ID for this "
                        "request.", scope_ids, path_scope_id)

        if path_scope_id:
            scope_ids = [path_scope_id]

        policy.authorize(
            flask.request.context,
            'schedule:get_task_reprocesses',
            {'tenant_id': flask.request.context.project_id or scope_ids}
        )

        if not isinstance(scope_ids, list):
            scope_ids = [scope_ids]

        if order not in ACCEPTED_GET_REPROCESSING_REQUEST_ORDERS:
            raise http_exceptions.BadRequest(
                "The order [%s] is not valid. Accepted values are %s.",
                order, ACCEPTED_GET_REPROCESSING_REQUEST_ORDERS)

        schedules = self.schedule_reprocessing_db.get_all(
            identifier=scope_ids, remove_finished=False,
            offset=offset, limit=limit, order=order)

        return {
            'results': [{
                'scope_id': s.identifier,
                'reason': s.reason,
                'start_reprocess_time': s.start_reprocess_time.isoformat(),
                'end_reprocess_time': s.end_reprocess_time.isoformat(),
                'current_reprocess_time':
                    s.current_reprocess_time.isoformat() if
                    s.current_reprocess_time else "",
            } for s in schedules]}
コード例 #4
0
    def _test_multiple_add_input_schema_x(self, location):
        @api_utils.add_input_schema(
            location, {
                voluptuous.Required('arg_one', default='one'):
                api_utils.SingleQueryParam(str)
                if location == 'query' else str,
            })
        @api_utils.add_input_schema(
            location, {
                voluptuous.Required('arg_two', default='two'):
                api_utils.SingleQueryParam(str)
                if location == 'query' else str,
            })
        def test_func(self, arg_one=None, arg_two=None):
            self.assertEqual(arg_one, 'one')
            self.assertEqual(arg_two, 'two')

        self.assertEqual(len(test_func.input_schema.schema.keys()), 2)
        self.assertEqual(
            sorted(list(test_func.input_schema.schema.keys())),
            ['arg_one', 'arg_two'],
        )
コード例 #5
0
    def test_simple_add_input_schema_query(self):
        @api_utils.add_input_schema(
            'query', {
                voluptuous.Required('arg_one', default='one'):
                api_utils.SingleQueryParam(str),
            })
        def test_func(self, arg_one=None):
            self.assertEqual(arg_one, 'one')

        self.assertEqual(len(test_func.input_schema.schema.keys()), 1)
        self.assertEqual(
            list(test_func.input_schema.schema.keys())[0], 'arg_one')

        with mock.patch('flask.request') as m:
            m.args = MultiDict({})
            test_func(self)
            m.args = MultiDict({'arg_one': 'one'})
            test_func(self)
コード例 #6
0
    def test_simple_add_input_schema_body(self):
        @api_utils.add_input_schema(
            'body', {
                voluptuous.Required('arg_one', default='one'):
                api_utils.SingleQueryParam(str),
            })
        def test_func(self, arg_one=None):
            self.assertEqual(arg_one, 'one')

        self.assertEqual(len(test_func.input_schema.schema.keys()), 1)
        self.assertEqual(
            list(test_func.input_schema.schema.keys())[0], 'arg_one')

        with mock.patch('flask.request') as m:
            m.get_json.return_value = {}
            test_func(self)

        with mock.patch('flask.request') as m:
            m.get_json.return_value = {'arg_one': 'one'}
            test_func(self)
コード例 #7
0
ファイル: example.py プロジェクト: Allen-labs/cloudkitty
class Example(flask_restful.Resource):
    @api_utils.add_output_schema({
        voluptuous.Required(
            'message',
            default='This is an example endpoint',
        ):
        api_utils.get_string_type(),
    })
    def get(self):
        policy.authorize(flask.request.context, 'example:get_example', {})
        return {}

    @api_utils.add_input_schema(
        'query', {
            voluptuous.Required('fruit'): api_utils.SingleQueryParam(str),
        })
    def put(self, fruit=None):
        policy.authorize(flask.request.context, 'example:submit_fruit', {})
        if not fruit:
            raise http_exceptions.BadRequest('You must submit a fruit', )
        if fruit not in ['banana', 'strawberry']:
            raise http_exceptions.Forbidden(
                'You submitted a forbidden fruit', )
        return {
            'message': 'Your fruit is a ' + fruit,
        }

    @api_utils.add_input_schema(
        'body', {
            voluptuous.Required('fruit'): api_utils.get_string_type(),
        })
    def post(self, fruit=None):
        policy.authorize(flask.request.context, 'example:submit_fruit', {})
        if not fruit:
            raise http_exceptions.BadRequest('You must submit a fruit', )
        if fruit not in ['banana', 'strawberry']:
            raise http_exceptions.Forbidden(
                'You submitted a forbidden fruit', )
        return {
            'message': 'Your fruit is a ' + fruit,
        }
コード例 #8
0
class ScopeState(base.BaseResource):
    @classmethod
    def reload(cls):
        super(ScopeState, cls).reload()
        cls._client = messaging.get_client()
        cls._storage_state = storage_state.StateManager()

    @api_utils.paginated
    @api_utils.add_input_schema(
        'query', {
            voluptuous.Optional('scope_id', default=[]):
            api_utils.MultiQueryParam(str),
            voluptuous.Optional('scope_key', default=[]):
            api_utils.MultiQueryParam(str),
            voluptuous.Optional('fetcher', default=[]):
            api_utils.MultiQueryParam(str),
            voluptuous.Optional('collector', default=[]):
            api_utils.MultiQueryParam(str),
            voluptuous.Optional('active', default=[]):
            api_utils.MultiQueryParam(int),
        })
    @api_utils.add_output_schema({
        'results': [{
            voluptuous.Required('scope_id'):
            vutils.get_string_type(),
            voluptuous.Required('scope_key'):
            vutils.get_string_type(),
            voluptuous.Required('fetcher'):
            vutils.get_string_type(),
            voluptuous.Required('collector'):
            vutils.get_string_type(),
            voluptuous.Optional('last_processed_timestamp'):
            vutils.get_string_type(),
            # This "state" property should be removed in the next release.
            voluptuous.Optional('state'):
            vutils.get_string_type(),
            voluptuous.Required('active'):
            bool,
            voluptuous.Optional('scope_activation_toggle_date'):
            vutils.get_string_type(),
        }]
    })
    def get(self,
            offset=0,
            limit=100,
            scope_id=None,
            scope_key=None,
            fetcher=None,
            collector=None,
            active=None):

        policy.authorize(
            flask.request.context, 'scope:get_state',
            {'project_id': scope_id or flask.request.context.project_id})
        results = self._storage_state.get_all(identifier=scope_id,
                                              scope_key=scope_key,
                                              fetcher=fetcher,
                                              collector=collector,
                                              offset=offset,
                                              limit=limit,
                                              active=active)

        if len(results) < 1:
            raise http_exceptions.NotFound(
                "No resource found for provided filters.")
        return {
            'results': [{
                'scope_id':
                r.identifier,
                'scope_key':
                r.scope_key,
                'fetcher':
                r.fetcher,
                'collector':
                r.collector,
                'state':
                r.last_processed_timestamp.isoformat(),
                'last_processed_timestamp':
                r.last_processed_timestamp.isoformat(),
                'active':
                r.active,
                'scope_activation_toggle_date':
                r.scope_activation_toggle_date.isoformat()
                if r.scope_activation_toggle_date else None
            } for r in results]
        }

    @api_utils.add_input_schema(
        'body',
        {
            voluptuous.Exclusive('all_scopes', 'scope_selector'):
            voluptuous.Boolean(),
            voluptuous.Exclusive('scope_id', 'scope_selector'):
            api_utils.MultiQueryParam(str),
            voluptuous.Optional('scope_key', default=[]):
            api_utils.MultiQueryParam(str),
            voluptuous.Optional('fetcher', default=[]):
            api_utils.MultiQueryParam(str),
            voluptuous.Optional('collector', default=[]):
            api_utils.MultiQueryParam(str),
            voluptuous.Optional('last_processed_timestamp'):
            voluptuous.Coerce(tzutils.dt_from_iso),
            # This "state" property should be removed in the next release.
            voluptuous.Optional('state'):
            voluptuous.Coerce(tzutils.dt_from_iso),
        })
    def put(self,
            all_scopes=False,
            scope_id=None,
            scope_key=None,
            fetcher=None,
            collector=None,
            last_processed_timestamp=None,
            state=None):

        policy.authorize(
            flask.request.context, 'scope:reset_state',
            {'project_id': scope_id or flask.request.context.project_id})

        if not all_scopes and scope_id is None:
            raise http_exceptions.BadRequest(
                "Either all_scopes or a scope_id should be specified.")

        if not state and not last_processed_timestamp:
            raise http_exceptions.BadRequest(
                "Variables 'state' and 'last_processed_timestamp' cannot be "
                "empty/None. We expect at least one of them.")
        if state:
            LOG.warning("The use of 'state' variable is deprecated, and will "
                        "be removed in the next upcomming release. You should "
                        "consider using 'last_processed_timestamp' variable.")

        results = self._storage_state.get_all(
            identifier=scope_id,
            scope_key=scope_key,
            fetcher=fetcher,
            collector=collector,
        )

        if len(results) < 1:
            raise http_exceptions.NotFound(
                "No resource found for provided filters.")

        serialized_results = [{
            'scope_id': r.identifier,
            'scope_key': r.scope_key,
            'fetcher': r.fetcher,
            'collector': r.collector,
        } for r in results]

        if not last_processed_timestamp:
            last_processed_timestamp = state
        self._client.cast({},
                          'reset_state',
                          res_data={
                              'scopes':
                              serialized_results,
                              'last_processed_timestamp':
                              last_processed_timestamp.isoformat()
                          })

        return {}, 202

    @api_utils.add_input_schema(
        'body', {
            voluptuous.Required('scope_id'): api_utils.SingleQueryParam(str),
            voluptuous.Optional('scope_key'): api_utils.SingleQueryParam(str),
            voluptuous.Optional('fetcher'): api_utils.SingleQueryParam(str),
            voluptuous.Optional('collector'): api_utils.SingleQueryParam(str),
            voluptuous.Optional('active'): api_utils.SingleQueryParam(bool),
        })
    @api_utils.add_output_schema({
        voluptuous.Required('scope_id'):
        vutils.get_string_type(),
        voluptuous.Required('scope_key'):
        vutils.get_string_type(),
        voluptuous.Required('fetcher'):
        vutils.get_string_type(),
        voluptuous.Required('collector'):
        vutils.get_string_type(),
        # This "state" property should be removed in the next release.
        voluptuous.Required('state'):
        vutils.get_string_type(),
        voluptuous.Optional('last_processed_timestamp'):
        voluptuous.Coerce(tzutils.dt_from_iso),
        voluptuous.Required('active'):
        bool,
        voluptuous.Required('scope_activation_toggle_date'):
        vutils.get_string_type()
    })
    def patch(self,
              scope_id,
              scope_key=None,
              fetcher=None,
              collector=None,
              active=None):

        policy.authorize(
            flask.request.context, 'scope:patch_state',
            {'tenant_id': scope_id or flask.request.context.project_id})
        results = self._storage_state.get_all(identifier=scope_id, active=None)

        if len(results) < 1:
            raise http_exceptions.NotFound(
                "No resource found for provided filters.")

        if len(results) > 1:
            LOG.debug(
                "Too many resources found with the same scope_id [%s], "
                "scopes found: [%s].", scope_id, results)
            raise http_exceptions.NotFound("Too many resources found with "
                                           "the same scope_id: %s." % scope_id)

        scope_to_update = results[0]
        LOG.debug("Executing update of storage scope: [%s].", scope_to_update)

        self._storage_state.update_storage_scope(scope_to_update,
                                                 scope_key=scope_key,
                                                 fetcher=fetcher,
                                                 collector=collector,
                                                 active=active)

        storage_scopes = self._storage_state.get_all(identifier=scope_id,
                                                     active=active)
        update_storage_scope = storage_scopes[0]
        return {
            'scope_id':
            update_storage_scope.identifier,
            'scope_key':
            update_storage_scope.scope_key,
            'fetcher':
            update_storage_scope.fetcher,
            'collector':
            update_storage_scope.collector,
            'state':
            update_storage_scope.state.isoformat(),
            'last_processed_timestamp':
            update_storage_scope.last_processed_timestamp.isoformat(),
            'active':
            update_storage_scope.active,
            'scope_activation_toggle_date':
            update_storage_scope.scope_activation_toggle_date.isoformat()
        }

    @api_utils.add_input_schema(
        'body', {
            voluptuous.Required('scope_id'): api_utils.SingleQueryParam(str),
            voluptuous.Optional('scope_key'): api_utils.SingleQueryParam(str),
            voluptuous.Optional('fetcher'): api_utils.SingleQueryParam(str),
            voluptuous.Optional('collector'): api_utils.SingleQueryParam(str),
            voluptuous.Optional('active'): api_utils.SingleQueryParam(bool),
        })
    @api_utils.add_output_schema({
        voluptuous.Required('scope_id'):
        vutils.get_string_type(),
        voluptuous.Required('scope_key'):
        vutils.get_string_type(),
        voluptuous.Required('fetcher'):
        vutils.get_string_type(),
        voluptuous.Required('collector'):
        vutils.get_string_type(),
        # This "state" property should be removed in the next release.
        voluptuous.Required('state'):
        vutils.get_string_type(),
        voluptuous.Optional('last_processed_timestamp'):
        voluptuous.Coerce(tzutils.dt_from_iso),
        voluptuous.Required('active'):
        bool,
        voluptuous.Required('scope_activation_toggle_date'):
        vutils.get_string_type()
    })
    def post(self,
             scope_id,
             scope_key=None,
             fetcher=None,
             collector=None,
             active=None):

        policy.authorize(
            flask.request.context, 'scope:post_state',
            {'tenant_id': scope_id or flask.request.context.project_id})
        results = self._storage_state.get_all(identifier=scope_id)

        if len(results) >= 1:
            LOG.debug(
                "There is already a scope with ID [%s], "
                "scopes found: [%s].", scope_id, results)
            raise http_exceptions.NotFound("Cannot create a scope with an "
                                           "already existing scope_id: %s." %
                                           scope_id)

        LOG.debug(
            "Creating storage scope with data: [scope_id=%s, "
            "scope_key=%s, fetcher=%s, collector=%s, active=%s].", scope_id,
            scope_key, fetcher, collector, active)

        self._storage_state.create_scope(scope_id,
                                         None,
                                         fetcher=fetcher,
                                         collector=collector,
                                         scope_key=scope_key,
                                         active=active)

        storage_scopes = self._storage_state.get_all(identifier=scope_id)

        update_storage_scope = storage_scopes[0]
        last_processed_timestamp = None
        if update_storage_scope.last_processed_timestamp.isoformat():
            last_processed_timestamp =\
                update_storage_scope.last_processed_timestamp.isoformat()

        return {
            'scope_id':
            update_storage_scope.identifier,
            'scope_key':
            update_storage_scope.scope_key,
            'fetcher':
            update_storage_scope.fetcher,
            'collector':
            update_storage_scope.collector,
            'state':
            last_processed_timestamp,
            'last_processed_timestamp':
            last_processed_timestamp,
            'active':
            update_storage_scope.active,
            'scope_activation_toggle_date':
            update_storage_scope.scope_activation_toggle_date.isoformat()
        }
コード例 #9
0
 def test_str_list_to_int(self):
     self.assertEqual(api_utils.SingleQueryParam(str)([42]), '42')
コード例 #10
0
 def test_int_list_to_int(self):
     self.assertEqual(api_utils.SingleQueryParam(int)([42]), 42)
コード例 #11
0
 def test_single_str_to_int(self):
     self.assertEqual(api_utils.SingleQueryParam(str)(42), '42')
コード例 #12
0
 def test_single_int_to_int(self):
     self.assertEqual(api_utils.SingleQueryParam(int)(42), 42)
コード例 #13
0
class DataFrameList(base.BaseResource):
    @api_utils.add_input_schema('body', {
        voluptuous.Required('dataframes'): [dataframe.DataFrame.from_dict],
    })
    def post(self, dataframes=[]):
        policy.authorize(
            flask.request.context,
            'dataframes:add',
            {},
        )

        if not dataframes:
            raise http_exceptions.BadRequest(
                "Parameter dataframes must not be empty.")

        self._storage.push(dataframes)

        return {}, 204

    @api_utils.paginated
    @api_utils.add_input_schema(
        'query', {
            voluptuous.Optional('begin'):
            api_utils.SingleQueryParam(tzutils.dt_from_iso),
            voluptuous.Optional('end'):
            api_utils.SingleQueryParam(tzutils.dt_from_iso),
            voluptuous.Optional('filters'):
            api_utils.SingleDictQueryParam(str, str),
        })
    @api_utils.add_output_schema({
        voluptuous.Required('total'):
        int,
        voluptuous.Required('dataframes'): [dataframe.DataFrame.as_dict],
    })
    def get(self, offset=0, limit=100, begin=None, end=None, filters=None):

        policy.authorize(
            flask.request.context,
            'dataframes:get',
            {'tenant_id': flask.request.context.project_id},
        )

        begin = begin or tzutils.get_month_start()
        end = end or tzutils.get_next_month()

        if filters and 'type' in filters:
            metric_types = [filters.pop('type')]
        else:
            metric_types = None

        if not flask.request.context.is_admin:
            if flask.request.context.project_id is None:
                # Unscoped non-admin user
                return {'total': 0, 'dataframes': []}
            scope_key = CONF.collect.scope_key
            if filters:
                filters[scope_key] = flask.request.context.project_id
            else:
                filters = {scope_key: flask.request.context.project_id}

        results = self._storage.retrieve(
            begin=begin,
            end=end,
            filters=filters,
            metric_types=metric_types,
            offset=offset,
            limit=limit,
        )

        if results['total'] < 1:
            raise http_exceptions.NotFound(
                "No resource found for provided filters.")

        return {
            'total': results['total'],
            'dataframes': results['dataframes'],
        }
コード例 #14
0
class Summary(base.BaseResource):
    """Resource allowing to retrieve a rating summary."""
    @api_utils.paginated
    @api_utils.add_input_schema(
        'query', {
            voluptuous.Optional('response_format'):
            api_utils.SingleQueryParam(str),
            voluptuous.Optional('custom_fields'):
            api_utils.SingleQueryParam(str),
            voluptuous.Optional('groupby'):
            api_utils.MultiQueryParam(str),
            voluptuous.Optional('filters'):
            api_utils.MultiDictQueryParam(str, str),
            voluptuous.Optional('begin'):
            api_utils.SingleQueryParam(tzutils.dt_from_iso),
            voluptuous.Optional('end'):
            api_utils.SingleQueryParam(tzutils.dt_from_iso),
        })
    def get(self,
            response_format=TABLE_RESPONSE_FORMAT,
            custom_fields=None,
            groupby=None,
            filters={},
            begin=None,
            end=None,
            offset=0,
            limit=100):

        if response_format not in ALL_RESPONSE_FORMATS:
            raise voluptuous.Invalid("Invalid response format [%s]. Valid "
                                     "format are [%s]." %
                                     (response_format, ALL_RESPONSE_FORMATS))

        policy.authorize(flask.request.context, 'summary:get_summary',
                         {'project_id': flask.request.context.project_id})
        begin = begin or tzutils.get_month_start()
        end = end or tzutils.get_next_month()

        if not flask.request.context.is_admin:
            if flask.request.context.project_id is None:
                # Unscoped non-admin user
                return {
                    'total': 0,
                    'columns': [],
                    'results': [],
                }
            filters['project_id'] = flask.request.context.project_id

        metric_types = filters.pop('type', [])
        if not isinstance(metric_types, list):
            metric_types = [metric_types]

        arguments = {
            'begin': begin,
            'end': end,
            'groupby': groupby,
            'filters': filters,
            'metric_types': metric_types,
            'offset': offset,
            'limit': limit,
            'paginate': True
        }
        if custom_fields:
            arguments['custom_fields'] = custom_fields

        total = self._storage.total(**arguments)

        return self.generate_response(response_format, total)

    @staticmethod
    def generate_response(response_format, total):
        response = {'total': total['total']}
        if response_format == TABLE_RESPONSE_FORMAT:
            columns = []
            if len(total['results']) > 0:
                columns = list(total['results'][0].keys())

            response['columns'] = columns
            response['results'] = [
                list(res.values()) for res in total['results']
            ]

        elif response_format == OBJECT_RESPONSE_FORMAT:
            response['results'] = total['results']

        response['format'] = response_format
        return response
コード例 #15
0
ファイル: reprocess.py プロジェクト: openstack/cloudkitty
class ReprocessSchedulerPostApi(base.BaseResource):
    def __init__(self, *args, **kwargs):
        super(ReprocessSchedulerPostApi, self).__init__(*args, **kwargs)
        self.storage_state_manager = storage_state.StateManager()
        self.schedule_reprocessing_db = storage_state.ReprocessingSchedulerDb()

    @api_utils.add_input_schema('body', {
        voluptuous.Required('scope_ids'): api_utils.MultiQueryParam(str),
        voluptuous.Required('start_reprocess_time'):
            voluptuous.Coerce(dt_from_iso_as_utc),
        voluptuous.Required('end_reprocess_time'):
            voluptuous.Coerce(dt_from_iso_as_utc),
        voluptuous.Required('reason'): api_utils.SingleQueryParam(str),
    })
    def post(self, scope_ids=[], start_reprocess_time=None,
             end_reprocess_time=None, reason=None):

        policy.authorize(
            flask.request.context,
            'schedule:task_reprocesses',
            {'tenant_id': flask.request.context.project_id or scope_ids}
        )

        ReprocessSchedulerPostApi.validate_inputs(
            end_reprocess_time, reason, scope_ids, start_reprocess_time)

        if ALL_SCOPES_OPTION in scope_ids:
            scope_ids = []

        if not isinstance(scope_ids, list):
            scope_ids = [scope_ids]

        all_scopes_to_reprocess = self.storage_state_manager.get_all(
            identifier=scope_ids, offset=None, limit=None)

        ReprocessSchedulerPostApi.check_if_there_are_invalid_scopes(
            all_scopes_to_reprocess, end_reprocess_time, scope_ids,
            start_reprocess_time)

        ReprocessSchedulerPostApi.validate_start_end_for_reprocessing(
            all_scopes_to_reprocess, end_reprocess_time, start_reprocess_time)

        self.validate_reprocessing_schedules_overlaps(
            all_scopes_to_reprocess, end_reprocess_time, start_reprocess_time)

        for scope in all_scopes_to_reprocess:
            schedule = ReprocessingScheduler(
                identifier=scope.identifier, reason=reason,
                start_reprocess_time=start_reprocess_time,
                end_reprocess_time=end_reprocess_time)

            LOG.debug("Persisting scope reprocessing schedule [%s].", schedule)
            self.schedule_reprocessing_db.persist(schedule)

        return {}, 202

    @staticmethod
    def validate_inputs(
            end_reprocess_time, reason, scope_ids, start_reprocess_time):
        ReprocessSchedulerPostApi.validate_scope_ids(scope_ids)

        if not reason.strip():
            raise http_exceptions.BadRequest(
                "Empty or blank reason text is not allowed. Please, do "
                "inform/register the reason for the reprocessing of a "
                "previously processed timestamp.")
        if end_reprocess_time < start_reprocess_time:
            raise http_exceptions.BadRequest(
                "End reprocessing timestamp [%s] cannot be less than "
                "start reprocessing timestamp [%s]."
                % (start_reprocess_time, end_reprocess_time))

    @staticmethod
    def validate_scope_ids(scope_ids):
        option_all_selected = False
        for s in scope_ids:
            if s == ALL_SCOPES_OPTION:
                option_all_selected = True
                continue

        if option_all_selected and len(scope_ids) != 1:
            raise http_exceptions.BadRequest(
                "Cannot use 'ALL' with scope ID [%s]. Either schedule a "
                "reprocessing for all active scopes using 'ALL' option, "
                "or inform only the scopes you desire to schedule a "
                "reprocessing." % scope_ids)

    @staticmethod
    def check_if_there_are_invalid_scopes(
            all_scopes_to_reprocess, end_reprocess_time, scope_ids,
            start_reprocess_time):

        invalid_scopes = []
        for s in scope_ids:
            scope_exist_in_db = False
            for scope_to_reprocess in all_scopes_to_reprocess:
                if s == scope_to_reprocess.identifier:
                    scope_exist_in_db = True
                    break

            if not scope_exist_in_db:
                invalid_scopes.append(s)

        if invalid_scopes:
            raise http_exceptions.BadRequest(
                "Scopes %s scheduled to reprocess [start=%s, end=%s] "
                "do not exist."
                % (invalid_scopes, start_reprocess_time, end_reprocess_time))

    @staticmethod
    def validate_start_end_for_reprocessing(all_scopes_to_reprocess,
                                            end_reprocess_time,
                                            start_reprocess_time):

        for scope in all_scopes_to_reprocess:
            last_processed_timestamp = scope.last_processed_timestamp
            if start_reprocess_time > last_processed_timestamp:
                raise http_exceptions.BadRequest(
                    "Cannot execute a reprocessing [start=%s] for scope [%s] "
                    "starting after the last possible timestamp [%s]."
                    % (start_reprocess_time, scope, last_processed_timestamp))
            if end_reprocess_time > scope.last_processed_timestamp:
                raise http_exceptions.BadRequest(
                    "Cannot execute a reprocessing [end=%s] for scope [%s] "
                    "ending after the last possible timestamp [%s]."
                    % (end_reprocess_time, scope, last_processed_timestamp))

    def validate_reprocessing_schedules_overlaps(
            self, all_scopes_to_reprocess, end_reprocess_time,
            start_reprocess_time):

        scheduling_range = DateTimeRange(
            start_reprocess_time, end_reprocess_time)

        for scope_to_reprocess in all_scopes_to_reprocess:
            all_reprocessing_schedules = self.schedule_reprocessing_db.get_all(
                identifier=[scope_to_reprocess.identifier])

            LOG.debug("All schedules [%s] for reprocessing found for scope "
                      "[%s]", all_reprocessing_schedules, scope_to_reprocess)
            if not all_reprocessing_schedules:
                LOG.debug(
                    "No need to validate possible collision of reprocessing "
                    "for scope [%s] because it does not have active "
                    "reprocessing schedules." % scope_to_reprocess)
                continue

            for schedule in all_reprocessing_schedules:
                scheduled_range = DateTimeRange(
                    tzutils.local_to_utc(schedule.start_reprocess_time),
                    tzutils.local_to_utc(schedule.end_reprocess_time))

                try:
                    if scheduling_range.is_intersection(scheduled_range):
                        raise http_exceptions.BadRequest(
                            self.generate_overlap_error_message(
                                scheduled_range, scheduling_range,
                                scope_to_reprocess))
                except ValueError as e:
                    raise http_exceptions.BadRequest(
                        self.generate_overlap_error_message(
                            scheduled_range, scheduling_range,
                            scope_to_reprocess) + "Error: [%s]." % e)

    @staticmethod
    def generate_overlap_error_message(scheduled_range, scheduling_range,
                                       scope_to_reprocess):
        return "Cannot schedule a reprocessing for scope [%s] for " \
               "reprocessing time [%s], because it already has a schedule " \
               "for a similar time range [%s]." % (scope_to_reprocess,
                                                   scheduling_range,
                                                   scheduled_range)