Validates the period compatibility of reprocessing dates
Problem description =================== The reprocess API is accepting time windows that are not compatible with the configured collection period which causes some reprocessings to be endless or generating different values based on the time window the user inputs. Proposal ======== We propose to add a validation in the reprocess API to deny users to schedule a reprocess using a not compatible time window and suggest the nearest valid time window that the user can schedule a reprocess. Change-Id: I24745a612bbd4714a7793df1deced671c1d1c26a
This commit is contained in:
parent
386f086a8b
commit
0c1eabc364
@ -11,6 +11,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
|
from datetime import timedelta
|
||||||
from datetimerange import DateTimeRange
|
from datetimerange import DateTimeRange
|
||||||
import flask
|
import flask
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
@ -24,6 +25,7 @@ from cloudkitty import storage_state
|
|||||||
from cloudkitty.storage_state.models import ReprocessingScheduler
|
from cloudkitty.storage_state.models import ReprocessingScheduler
|
||||||
from cloudkitty.utils import tz as tzutils
|
from cloudkitty.utils import tz as tzutils
|
||||||
from cloudkitty.utils import validation as validation_utils
|
from cloudkitty.utils import validation as validation_utils
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
@ -91,6 +93,29 @@ class ReprocessSchedulerPostApi(base.BaseResource):
|
|||||||
|
|
||||||
return {}, 202
|
return {}, 202
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_date_period_overflow(date):
|
||||||
|
return int(date.timestamp() % cfg.CONF.collect.period)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_valid_period_date(date):
|
||||||
|
return date - timedelta(
|
||||||
|
seconds=ReprocessSchedulerPostApi.get_date_period_overflow(date))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_overflow_from_dates(start, end):
|
||||||
|
start_overflow = ReprocessSchedulerPostApi.get_date_period_overflow(
|
||||||
|
start)
|
||||||
|
end_overflow = ReprocessSchedulerPostApi.get_date_period_overflow(end)
|
||||||
|
if start_overflow or end_overflow:
|
||||||
|
valid_start = ReprocessSchedulerPostApi.get_valid_period_date(
|
||||||
|
start)
|
||||||
|
valid_end = ReprocessSchedulerPostApi.get_valid_period_date(end)
|
||||||
|
if valid_start == valid_end:
|
||||||
|
valid_end += timedelta(seconds=cfg.CONF.collect.period)
|
||||||
|
|
||||||
|
return [str(valid_start), str(valid_end)]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_inputs(
|
def validate_inputs(
|
||||||
end_reprocess_time, reason, scope_ids, start_reprocess_time):
|
end_reprocess_time, reason, scope_ids, start_reprocess_time):
|
||||||
@ -107,6 +132,14 @@ class ReprocessSchedulerPostApi(base.BaseResource):
|
|||||||
"start reprocessing timestamp [%s]."
|
"start reprocessing timestamp [%s]."
|
||||||
% (start_reprocess_time, end_reprocess_time))
|
% (start_reprocess_time, end_reprocess_time))
|
||||||
|
|
||||||
|
periods_overflows = ReprocessSchedulerPostApi.get_overflow_from_dates(
|
||||||
|
start_reprocess_time, end_reprocess_time)
|
||||||
|
if periods_overflows:
|
||||||
|
raise http_exceptions.BadRequest(
|
||||||
|
"The provided reprocess time window does not comply with "
|
||||||
|
"the configured collector period. A valid time window "
|
||||||
|
"near the provided one is %s" % periods_overflows)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_scope_ids(scope_ids):
|
def validate_scope_ids(scope_ids):
|
||||||
option_all_selected = False
|
option_all_selected = False
|
||||||
|
@ -31,9 +31,10 @@ class TestReprocessSchedulerPostApi(tests.TestCase):
|
|||||||
self.scope_ids = ["some-other-scope-id",
|
self.scope_ids = ["some-other-scope-id",
|
||||||
"5e56cb64-4980-4466-9fce-d0133c0c221e"]
|
"5e56cb64-4980-4466-9fce-d0133c0c221e"]
|
||||||
|
|
||||||
self.start_reprocess_time = tzutils.localized_now()
|
self.start_reprocess_time = self.endpoint.get_valid_period_date(
|
||||||
self.end_reprocess_time =\
|
tzutils.localized_now())
|
||||||
self.start_reprocess_time + datetime.timedelta(hours=1)
|
self.end_reprocess_time = self.endpoint.get_valid_period_date(
|
||||||
|
self.start_reprocess_time + datetime.timedelta(hours=1))
|
||||||
|
|
||||||
self.reason = "We are testing the reprocess API."
|
self.reason = "We are testing the reprocess API."
|
||||||
|
|
||||||
@ -100,6 +101,54 @@ class TestReprocessSchedulerPostApi(tests.TestCase):
|
|||||||
self.end_reprocess_time, self.reason, self.scope_ids,
|
self.end_reprocess_time, self.reason, self.scope_ids,
|
||||||
self.start_reprocess_time)
|
self.start_reprocess_time)
|
||||||
|
|
||||||
|
def test_validate_inputs_different_from_configured_period(self):
|
||||||
|
original_end_reprocess_time = self.end_reprocess_time
|
||||||
|
|
||||||
|
self.end_reprocess_time += datetime.timedelta(seconds=1)
|
||||||
|
|
||||||
|
expected_message = "400 Bad Request: The provided reprocess time " \
|
||||||
|
"window does not comply with the configured" \
|
||||||
|
" collector period. A valid time window near " \
|
||||||
|
"the provided one is ['%s', '%s']" % (
|
||||||
|
self.start_reprocess_time,
|
||||||
|
original_end_reprocess_time)
|
||||||
|
|
||||||
|
expected_message = re.escape(expected_message)
|
||||||
|
|
||||||
|
self.assertRaisesRegex(http_exceptions.BadRequest, expected_message,
|
||||||
|
self.endpoint.validate_inputs,
|
||||||
|
self.end_reprocess_time, self.reason,
|
||||||
|
self.scope_ids, self.start_reprocess_time)
|
||||||
|
|
||||||
|
self.end_reprocess_time = original_end_reprocess_time
|
||||||
|
self.endpoint.validate_inputs(
|
||||||
|
self.end_reprocess_time, self.reason, self.scope_ids,
|
||||||
|
self.start_reprocess_time)
|
||||||
|
|
||||||
|
def test_validate_time_window_smaller_than_configured_period(self):
|
||||||
|
start = datetime.datetime(year=2022, day=22, month=2, hour=10,
|
||||||
|
minute=10, tzinfo=tzutils._LOCAL_TZ)
|
||||||
|
end = datetime.datetime(year=2022, day=22, month=2, hour=10,
|
||||||
|
minute=20, tzinfo=tzutils._LOCAL_TZ)
|
||||||
|
expected_start = datetime.datetime(year=2022, day=22, month=2, hour=10,
|
||||||
|
tzinfo=tzutils._LOCAL_TZ)
|
||||||
|
expected_end = datetime.datetime(year=2022, day=22, month=2, hour=11,
|
||||||
|
tzinfo=tzutils._LOCAL_TZ)
|
||||||
|
|
||||||
|
expected_message = "400 Bad Request: The provided reprocess time " \
|
||||||
|
"window does not comply with the configured" \
|
||||||
|
" collector period. A valid time window near " \
|
||||||
|
"the provided one is ['%s', '%s']" % (
|
||||||
|
expected_start,
|
||||||
|
expected_end)
|
||||||
|
|
||||||
|
expected_message = re.escape(expected_message)
|
||||||
|
|
||||||
|
self.assertRaisesRegex(http_exceptions.BadRequest, expected_message,
|
||||||
|
self.endpoint.validate_inputs,
|
||||||
|
end, self.reason,
|
||||||
|
self.scope_ids, start)
|
||||||
|
|
||||||
def test_check_if_there_are_invalid_scopes(self):
|
def test_check_if_there_are_invalid_scopes(self):
|
||||||
all_scopes = self.generate_all_scopes_object()
|
all_scopes = self.generate_all_scopes_object()
|
||||||
|
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Add a validation to not allow users to schedule reprocesses via
|
||||||
|
``POST`` request on ``/v2/task/reprocesses`` using a time window not
|
||||||
|
compatible with the configured ``period`` in the collector.
|
Loading…
Reference in New Issue
Block a user