Example #1
0
def update(schedule_id, delta):
    """
    Updates the schedule with unique ID schedule_id. This only allows updating
    of fields in ScheduledCall.USER_UPDATE_FIELDS.

    :param schedule_id: a unique ID for a schedule
    :type  schedule_id: basestring
    :param delta:       a dictionary of keys with values that should be modified
                        on the schedule.
    :type  delta:       dict

    :return:    instance of ScheduledCall representing the post-update state
    :rtype      ScheduledCall

    :raise  exceptions.UnsupportedValue
    :raise  exceptions.MissingResource
    """
    unknown_keys = set(delta.keys()) - ScheduledCall.USER_UPDATE_FIELDS
    if unknown_keys:
        raise exceptions.UnsupportedValue(list(unknown_keys))

    delta['last_updated'] = time.time()

    try:
        spec = {'_id': ObjectId(schedule_id)}
    except InvalidId:
        raise exceptions.InvalidValue(['schedule_id'])
    schedule = ScheduledCall.get_collection().find_and_modify(
        query=spec, update={'$set': delta}, safe=True, new=True)
    if schedule is None:
        raise exceptions.MissingResource(schedule_id=schedule_id)
    return ScheduledCall.from_db(schedule)
Example #2
0
    def test_first_run_now(self):
        call = ScheduledCall('PT1M', 'pulp.tasks.dosomething')

        first_run_s = call._calculate_times()[1]

        # make sure this gives us a timestamp that reasonably represents "now"
        self.assertTrue(time.time() - first_run_s < 1)
Example #3
0
    def test_no_last_run(self):
        call = ScheduledCall('PT1M', 'pulp.tasks.dosomething')

        entry = call.as_schedule_entry()

        # celery actually calculates it, so we don't need to test the value
        self.assertTrue(isinstance(entry.last_run_at, datetime))
Example #4
0
    def test_get(self, mock_path, mock_ok, mock_utils_get):
        call = ScheduledCall('PT1M', 'pulp.tasks.frequent')
        mock_utils_get.return_value = [call]

        ret = self.controller._get(call.id)
        schedule = mock_ok.call_args[0][0]

        self.assertEqual(ret, mock_ok.return_value)
        self.assertEqual(len(mock_ok.call_args[0]), 1)

        # spot-check the schedule
        self.assertEqual(schedule['_id'], call.id)
        self.assertEqual(schedule['schedule'], 'PT1M')
        self.assertEqual(schedule['task'], 'pulp.tasks.frequent')
        self.assertEqual(schedule['_href'], mock_path.return_value)

        # next_run is calculated on-demand, and there is a small chance that it
        # will be re-calculated in the call.for_display() call as 1 second later
        # than it was calculated above. Thus we will test that equality here
        # with a tolerance of 1 second
        for_display = call.for_display()
        call_next_run = dateutils.parse_iso8601_datetime(call.next_run)
        display_next_run = dateutils.parse_iso8601_datetime(for_display['next_run'])
        self.assertTrue(display_next_run - call_next_run <= timedelta(seconds=1))

        # now check overall equality with the actual for_display value
        del schedule['_href']
        del schedule['next_run']
        del for_display['next_run']
        self.assertEqual(schedule, for_display)

        # make sure we called the manager layer correctly
        mock_utils_get.assert_called_once_with([call.id])
Example #5
0
def increment_failure_count(schedule_id):
    """
    Increment the number of consecutive failures, and if it has met or exceeded
    the threshold, disable the schedule.

    :param schedule_id: ID of the schedule whose count should be incremented
    :type  schedule_id: str
    """
    try:
        spec = {'_id': ObjectId(schedule_id)}
    except InvalidId:
        raise exceptions.InvalidValue(['schedule_id'])
    delta = {
        '$inc': {'consecutive_failures': 1},
        '$set': {'last_updated': time.time()},
    }
    schedule = ScheduledCall.get_collection().find_and_modify(
        query=spec, update=delta, new=True)
    if schedule:
        scheduled_call = ScheduledCall.from_db(schedule)
        if scheduled_call.failure_threshold is None or not scheduled_call.enabled:
            return
        if scheduled_call.consecutive_failures >= scheduled_call.failure_threshold:
            _logger.info(_('disabling schedule %(id)s with %(count)d consecutive failures') % {
                'id': schedule_id, 'count': scheduled_call.consecutive_failures
            })
            delta = {'$set': {
                'enabled': False,
                'last_updated': time.time(),
            }}
            ScheduledCall.get_collection().update(spec, delta)
Example #6
0
def increment_failure_count(schedule_id):
    """
    Increment the number of consecutive failures, and if it has met or exceeded
    the threshold, disable the schedule.

    :param schedule_id: ID of the schedule whose count should be incremented
    :type  schedule_id: str
    """
    try:
        spec = {'_id': ObjectId(schedule_id)}
    except InvalidId:
        raise exceptions.InvalidValue(['schedule_id'])
    delta = {
        '$inc': {'consecutive_failures': 1},
        '$set': {'last_updated': time.time()},
    }
    schedule = ScheduledCall.get_collection().find_and_modify(
        query=spec, update=delta, new=True)
    if schedule:
        scheduled_call = ScheduledCall.from_db(schedule)
        if scheduled_call.failure_threshold is None or not scheduled_call.enabled:
            return
        if scheduled_call.consecutive_failures >= scheduled_call.failure_threshold:
            _logger.info(_('disabling schedule %(id)s with %(count)d consecutive failures') % {
                'id': schedule_id, 'count': scheduled_call.consecutive_failures
            })
            delta = {'$set': {
                'enabled': False,
                'last_updated': time.time(),
            }}
            ScheduledCall.get_collection().update(spec, delta)
Example #7
0
    def test_now(self):
        call = ScheduledCall('PT1H', 'pulp.tasks.dosomething')

        now = datetime.utcnow().replace(tzinfo=dateutils.utc_tz())
        next_run = dateutils.parse_iso8601_datetime(call.calculate_next_run())

        self.assertTrue(next_run - now < timedelta(seconds=1))
Example #8
0
    def test_captures_scheduled_call(self):
        call = ScheduledCall('2014-01-19T17:15Z/PT1H',
                             'pulp.tasks.dosomething')
        entry = call.as_schedule_entry()

        self.assertTrue(hasattr(entry, '_scheduled_call'))
        self.assertTrue(entry._scheduled_call is call)
Example #9
0
    def test_run_every(self):
        call = ScheduledCall('2014-01-03T10:15Z/PT1H', 'pulp.tasks.dosomething')

        run_every_s = call._calculate_times()[3]

        # 1 hour, as specified in the ISO8601 string above
        self.assertEqual(run_every_s, 3600)
Example #10
0
    def test_with_months_duration(self, mock_time):
        """
        Test calculating the next run when the interval is a Duration object and uses months
        """
        last_runs = ('2015-01-01T10:00Z', '2015-02-01T10:00Z',
                     '2015-03-01T10:00Z', '2015-04-01T10:00Z')
        expected_next_runs = ('2015-02-01T10:00Z', '2015-03-01T10:00Z',
                              '2015-04-01T10:00Z', '2015-05-01T10:00Z')
        times = (
            1422784799.0,  # Just before 2015-02-01T10:00Z UTC
            1425203999.0,  # Just before 2015-03-01T10:00Z UTC
            1427882399.0,  # Just before 2015-04-01T10:00Z UTC
            1430474399.0,  # Just before 2015-05-01T10:00Z UTC
        )

        for last_run, current_time, expected_next_run in zip(
                last_runs, times, expected_next_runs):
            mock_time.return_value = current_time
            call = ScheduledCall('2014-12-01T10:00Z/P1M',
                                 'pulp.tasks.dosomething',
                                 total_run_count=2,
                                 last_run_at=last_run)
            next_run = call.calculate_next_run()

            self.assertEqual(
                dateutils.parse_iso8601_datetime(expected_next_run),
                dateutils.parse_iso8601_datetime(next_run))
Example #11
0
    def test_with_years_duration(self, mock_time):
        """
        Test calculating the next run when the interval is a Duration object and uses years
        """
        last_runs = ('2015-01-01T10:00Z', '2016-01-01T10:00Z',
                     '2017-01-01T10:00Z', '2018-01-01T10:00Z')
        expected_next_runs = ('2016-01-01T10:00Z', '2017-01-01T10:00Z',
                              '2018-01-01T10:00Z', '2019-01-01T10:00Z')
        times = (
            1451642000.0,  # Just before 2016-01-01T10:00Z UTC
            1483264000.0,  # Just before 2017-01-01T10:00Z UTC
            1514800000.0,  # Just before 2018-01-01T10:00Z UTC
            1546336000.0,  # Just before 2019-01-01T10:00Z UTC
        )

        for last_run, current_time, expected_next_run in zip(
                last_runs, times, expected_next_runs):
            mock_time.return_value = current_time
            call = ScheduledCall('2014-01-01T10:00Z/P1M',
                                 'pulp.tasks.dosomething',
                                 total_run_count=2,
                                 last_run_at=last_run)
            next_run = call.calculate_next_run()

            self.assertEqual(
                dateutils.parse_iso8601_datetime(expected_next_run),
                dateutils.parse_iso8601_datetime(next_run))
Example #12
0
    def create_schedule(cls,
                        action,
                        consumer_id,
                        units,
                        options,
                        schedule,
                        failure_threshold=None,
                        enabled=True):
        """
        Creates a new schedule for a consumer action

        :param action:          a unique identified for an action, one of
                                UNIT_INSTALL_ACTION, UNIT_UPDATE_ACTION,
                                UNIT_UNINSTALL_ACTION
        :type  action:          basestring
        :param consumer_id:     a unique ID for a consumer
        :type  consumer_id:     basestring
        :param units:           A list of content units to be installed, each as
                                a dict in the form:
                                    { type_id:<str>, unit_key:<dict> }
        :type  units:           list
        :param options:         a dictionary that will be passed to the
                                action-appropriate task as the "options"
                                argument
        :type  options:         dict
        :param schedule:        ISO8601 string representation of the schedule
        :type  schedule:        basestring
        :param failure_threshold:   optional positive integer indicating how
                                many times this schedule's execution can fail
                                before being automatically disabled.
        :type  failure_threshold:   int or NoneType
        :param enabled:         boolean indicating if this schedule should
                                be actively loaded and executed by the
                                scheduler. Defaults to True.
        :type  enabled:         bool
        :return:    instance of the new ScheduledCal
        :rtype:     pulp.server.db.models.dispatch.ScheduledCall

        :raise:     pulp.server.exceptions.MissingValue
        """
        cls._validate_consumer(consumer_id)
        utils.validate_initial_schedule_options(schedule, failure_threshold,
                                                enabled)
        if not units:
            raise MissingValue(['units'])

        task = ACTIONS_TO_TASKS[action]
        args = [consumer_id]
        kwargs = {'units': units, 'options': options}
        resource = Consumer.build_resource_tag(consumer_id)

        schedule = ScheduledCall(schedule,
                                 task,
                                 args=args,
                                 kwargs=kwargs,
                                 resource=resource,
                                 failure_threshold=failure_threshold,
                                 enabled=enabled)
        schedule.save()
        return schedule
Example #13
0
    def test_last_scheduled_run_no_first_run(self):
        call = ScheduledCall('PT1H', 'pulp.tasks.dosomething')

        last_scheduled_run_s = call._calculate_times()[4]
        first_run_s = call._calculate_times()[1]

        self.assertEqual(last_scheduled_run_s, first_run_s)
Example #14
0
 def tearDown(self):
     super(SchedulerTests, self).tearDown()
     ScheduledCall.get_collection().drop()
     self.scheduler = None
     dispatch_factory.coordinator = self._coordinator_factory
     self._coordinator_factory = None
     dispatch_factory._SCHEDULER = None
Example #15
0
def update(schedule_id, delta):
    """
    Updates the schedule with unique ID schedule_id. This only allows updating
    of fields in ScheduledCall.USER_UPDATE_FIELDS.

    :param schedule_id: a unique ID for a schedule
    :type  schedule_id: basestring
    :param delta:       a dictionary of keys with values that should be modified
                        on the schedule.
    :type  delta:       dict

    :return:    instance of ScheduledCall representing the post-update state
    :rtype      ScheduledCall

    :raise  exceptions.UnsupportedValue
    :raise  exceptions.MissingResource
    """
    unknown_keys = set(delta.keys()) - ScheduledCall.USER_UPDATE_FIELDS
    if unknown_keys:
        raise exceptions.UnsupportedValue(list(unknown_keys))

    delta['last_updated'] = time.time()

    try:
        spec = {'_id': ObjectId(schedule_id)}
    except InvalidId:
        raise exceptions.InvalidValue(['schedule_id'])
    schedule = ScheduledCall.get_collection().find_and_modify(
        query=spec, update={'$set': delta}, safe=True, new=True)
    if schedule is None:
        raise exceptions.MissingResource(schedule_id=schedule_id)
    return ScheduledCall.from_db(schedule)
Example #16
0
    def test_since_first(self):
        call = ScheduledCall('2014-01-03T10:15Z/PT1H', 'pulp.tasks.dosomething')

        since_first = call._calculate_times()[2]
        now = time.time()

        self.assertTrue(since_first + 1388744100 - now < 1)
Example #17
0
 def setUp(self):
     super(TestGet, self).setUp()
     self.manager = ConsumerScheduleManager()
     self.calls = [
         ScheduledCall('PT1H', ACTIONS_TO_TASKS[UNIT_INSTALL_ACTION]),
         ScheduledCall('PT4H', ACTIONS_TO_TASKS[UNIT_UPDATE_ACTION])
     ]
Example #18
0
    def test_get(self, mock_path, mock_ok, mock_utils_get):
        call = ScheduledCall('PT1M', 'pulp.tasks.frequent')
        mock_utils_get.return_value = [call]

        ret = self.controller._get(call.id)
        schedule = mock_ok.call_args[0][0]

        self.assertEqual(ret, mock_ok.return_value)
        self.assertEqual(len(mock_ok.call_args[0]), 1)

        # spot-check the schedule
        self.assertEqual(schedule['_id'], call.id)
        self.assertEqual(schedule['schedule'], 'PT1M')
        self.assertEqual(schedule['task'], 'pulp.tasks.frequent')
        self.assertEqual(schedule['_href'], mock_path.return_value)

        # next_run is calculated on-demand, and there is a small chance that it
        # will be re-calculated in the call.for_display() call as 1 second later
        # than it was calculated above. Thus we will test that equality here
        # with a tolerance of 1 second
        for_display = call.for_display()
        call_next_run = dateutils.parse_iso8601_datetime(call.next_run)
        display_next_run = dateutils.parse_iso8601_datetime(
            for_display['next_run'])
        self.assertTrue(
            display_next_run - call_next_run <= timedelta(seconds=1))

        # now check overall equality with the actual for_display value
        del schedule['_href']
        del schedule['next_run']
        del for_display['next_run']
        self.assertEqual(schedule, for_display)

        # make sure we called the manager layer correctly
        mock_utils_get.assert_called_once_with([call.id])
Example #19
0
def delete_by_resource(resource):
    """
    Deletes all schedules for the specified resource

    :param resource:    string indicating a unique resource
    :type  resource:    basestring
    """
    ScheduledCall.get_collection().remove({'resource': resource}, safe=True)
Example #20
0
    def test_first_run_scheduled(self):
        call = ScheduledCall('2014-01-03T10:15Z/PT1H', 'pulp.tasks.dosomething')

        first_run_s = call._calculate_times()[1]

        # make sure this gives us a timestamp for the date and time
        # specified above
        self.assertEqual(first_run_s, 1388744100)
Example #21
0
    def test_last_scheduled_run_with_first_run(self, mock_time):
        # specify a start time and current time such that we know the difference
        mock_time.return_value = 1389307330.966561
        call = ScheduledCall('2014-01-09T17:15Z/PT1H', 'pulp.tasks.dosomething')

        last_scheduled_run_s = call._calculate_times()[4]

        self.assertEqual(last_scheduled_run_s, 1389305700)
Example #22
0
def delete_by_resource(resource):
    """
    Deletes all schedules for the specified resource

    :param resource:    string indicating a unique resource
    :type  resource:    basestring
    """
    ScheduledCall.get_collection().remove({'resource': resource}, safe=True)
Example #23
0
    def test_no_runs(self):
        call = ScheduledCall('PT1H', 'pulp.tasks.dosomething')
        entry = call.as_schedule_entry()

        is_due, seconds = entry.is_due()

        self.assertTrue(is_due)
        # make sure this is very close to one hour
        self.assertTrue(3600 - seconds < 1)
Example #24
0
    def test_expected_runs_positive(self, mock_time):
        # specify a start time and current time such that we know the difference
        mock_time.return_value = 1389307330.966561
        call = ScheduledCall('2014-01-09T17:15Z/PT1H', 'pulp.tasks.dosomething')

        expected_runs = call._calculate_times()[5]

        # we know that it's been more than 5 hours since the first scheduled run
        self.assertEqual(expected_runs, 5)
Example #25
0
    def create(cls,
               repo_id,
               distributor_id,
               publish_options,
               schedule,
               failure_threshold=None,
               enabled=True):
        """
        Create a new scheduled publish for the given repository and distributor.

        :param repo_id:         unique ID for a repository
        :type  repo_id:         basestring
        :param distributor_id:  unique ID for a distributor
        :type  distributor_id:  basestring
        :param publish_options: dictionary that contains the key 'override_config',
                                whose value should be passed as the 'overrides'
                                parameter to the publish task. This wasn't originally
                                documented, so it isn't clear why overrides value
                                couldn't be passed directly.
        :type  sync_options:    dict
        :param schedule_data:   dictionary that contains the key 'schedule', whose
                                value is an ISO8601 string. This wasn't originally
                                documented, so it isn't clear why the string itself
                                couldn't have been passed directly.
        :type  schedule_data:   dict

        :return:    new schedule instance
        :rtype:     pulp.server.db.model.dispatch.ScheduledCall
        """
        dist = model.Distributor.objects.get_or_404(
            repo_id=repo_id, distributor_id=distributor_id)
        utils.validate_keys(publish_options, _PUBLISH_OPTION_KEYS)
        utils.validate_initial_schedule_options(schedule, failure_threshold,
                                                enabled)

        task = repo_controller.queue_publish.name
        args = [repo_id, distributor_id]
        kwargs = {'overrides': publish_options['override_config']}
        schedule = ScheduledCall(schedule,
                                 task,
                                 args=args,
                                 kwargs=kwargs,
                                 resource=dist.resource_tag,
                                 failure_threshold=failure_threshold,
                                 enabled=enabled)
        schedule.save()

        try:
            model.Distributor.objects.get_or_404(repo_id=repo_id,
                                                 distributor_id=distributor_id)
        except exceptions.MissingResource:
            # back out of this whole thing, since the distributor disappeared
            utils.delete(schedule.id)
            raise

        return schedule
Example #26
0
    def test_new(self, mock_get_collection):
        mock_insert = mock_get_collection.return_value.insert
        call = ScheduledCall('PT1M', 'pulp.tasks.dosomething')

        call.save()

        expected = call.as_dict()
        expected['_id'] = bson.ObjectId(expected['_id'])
        mock_insert.assert_called_once_with(expected, safe=True)
        self.assertFalse(call._new)
Example #27
0
    def test_existing(self, mock_get_collection):
        mock_update = mock_get_collection.return_value.update
        fake_id = bson.ObjectId()
        call = ScheduledCall('PT1M', 'pulp.tasks.dosomething', id=fake_id)

        call.save()

        expected = call.as_dict()
        del expected['_id']
        mock_update.assert_called_once_with({'_id': fake_id}, expected)
Example #28
0
    def create(cls,
               repo_id,
               importer_id,
               sync_options,
               schedule,
               failure_threshold=None,
               enabled=True):
        """
        Create a new sync schedule for a given repository using the given importer.

        :param repo_id:         unique ID for a repository
        :type  repo_id:         basestring
        :param importer_id:     unique ID for an importer
        :type  importer_id:     basestring
        :param sync_options:    dictionary that contains the key 'override_config',
                                whose value should be passed as the 'overrides'
                                parameter to the sync task. This wasn't originally
                                documented, so it isn't clear why overrides value
                                couldn't be passed directly.
        :type  sync_options:    dict
        :param schedule_data:   dictionary that contains the key 'schedule', whose
                                value is an ISO8601 string. This wasn't originally
                                documented, so it isn't clear why the string itself
                                couldn't have been passed directly.
        :type  schedule_data:   dict

        :return:    new schedule instance
        :rtype:     pulp.server.db.model.dispatch.ScheduledCall
        """
        # validate the input
        importer_controller.get_valid_importer(repo_id, importer_id)
        utils.validate_keys(sync_options, _SYNC_OPTION_KEYS)
        utils.validate_initial_schedule_options(schedule, failure_threshold,
                                                enabled)

        task = repo_controller.queue_sync_with_auto_publish.name
        args = [repo_id]
        kwargs = {'overrides': sync_options['override_config']}
        resource = importer_controller.build_resource_tag(repo_id, importer_id)
        schedule = ScheduledCall(schedule,
                                 task,
                                 args=args,
                                 kwargs=kwargs,
                                 resource=resource,
                                 failure_threshold=failure_threshold,
                                 enabled=enabled)
        schedule.save()
        try:
            importer_controller.get_valid_importer(repo_id, importer_id)
        except exceptions.MissingResource:
            # back out of this whole thing, since the importer disappeared
            utils.delete(schedule.id)
            raise

        return schedule
Example #29
0
    def test_first_run_saved(self):
        """
        Test that when the first run is passed in from historical data.
        """
        call = ScheduledCall('PT1H', 'pulp.tasks.dosomething', first_run='2014-01-03T10:15Z')

        first_run_s = call._calculate_times()[1]

        # make sure this gives us a timestamp for the date and time
        # specified above
        self.assertEqual(first_run_s, 1388744100)
Example #30
0
def delete(schedule_id):
    """
    Deletes the schedule with unique ID schedule_id

    :param schedule_id: a unique ID for a schedule
    :type  schedule_id: basestring
    """
    try:
        ScheduledCall.get_collection().remove({'_id': ObjectId(schedule_id)}, safe=True)
    except InvalidId:
        raise exceptions.InvalidValue(['schedule_id'])
Example #31
0
    def create_schedule(cls, action, consumer_id, units, options, schedule, failure_threshold=None, enabled=True):
        """
        Creates a new schedule for a consumer action

        :param action:          a unique identified for an action, one of
                                UNIT_INSTALL_ACTION, UNIT_UPDATE_ACTION,
                                UNIT_UNINSTALL_ACTION
        :type  action:          basestring
        :param consumer_id:     a unique ID for a consumer
        :type  consumer_id:     basestring
        :param units:           A list of content units to be installed, each as
                                a dict in the form:
                                    { type_id:<str>, unit_key:<dict> }
        :type  units:           list
        :param options:         a dictionary that will be passed to the
                                action-appropriate task as the "options"
                                argument
        :type  options:         dict
        :param schedule:        ISO8601 string representation of the schedule
        :type  schedule:        basestring
        :param failure_threshold:   optional positive integer indicating how
                                many times this schedule's execution can fail
                                before being automatically disabled.
        :type  failure_threshold:   int or NoneType
        :param enabled:         boolean indicating if this schedule should
                                be actively loaded and executed by the
                                scheduler. Defaults to True.
        :type  enabled:         bool
        :return:    instance of the new ScheduledCal
        :rtype:     pulp.server.db.models.dispatch.ScheduledCall

        :raise:     pulp.server.exceptions.MissingValue
        """
        cls._validate_consumer(consumer_id)
        utils.validate_initial_schedule_options(schedule, failure_threshold, enabled)
        if not units:
            raise MissingValue(["units"])

        task = ACTIONS_TO_TASKS[action]
        args = [consumer_id]
        kwargs = {"units": units, "options": options}
        resource = Consumer.build_resource_tag(consumer_id)

        schedule = ScheduledCall(
            schedule,
            task,
            args=args,
            kwargs=kwargs,
            resource=resource,
            failure_threshold=failure_threshold,
            enabled=enabled,
        )
        schedule.save()
        return schedule
Example #32
0
    def test_expected_runs_future(self, mock_time):
        # specify a start time and current time such that the start appears to
        # be in the future
        mock_time.return_value = 1389307330.966561
        call = ScheduledCall('2014-01-19T17:15Z/PT1H', 'pulp.tasks.dosomething')

        expected_runs = call._calculate_times()[5]

        # the first run is scheduled in the future (relative to the mock time),
        # so there should not be any runs.
        self.assertEqual(expected_runs, 0)
Example #33
0
    def test_future(self, mock_time):
        mock_time.return_value = 1389307330.966561
        call = ScheduledCall('2014-01-19T17:15Z/PT1H', 'pulp.tasks.dosomething')

        next_run = call.calculate_next_run()

        # make sure the next run is equal to the specified first run.
        # don't want to compare a generated ISO8601 string directly, because there
        # could be subtle variations that are valid but break string equality.
        self.assertEqual(dateutils.parse_iso8601_interval(call.iso_schedule)[1],
                         dateutils.parse_iso8601_datetime(next_run))
Example #34
0
def delete(schedule_id):
    """
    Deletes the schedule with unique ID schedule_id

    :param schedule_id: a unique ID for a schedule
    :type  schedule_id: basestring
    """
    try:
        ScheduledCall.get_collection().remove({'_id': ObjectId(schedule_id)},
                                              safe=True)
    except InvalidId:
        raise exceptions.InvalidValue(['schedule_id'])
Example #35
0
 def remove(self, schedule_id):
     """
     Remove a scheduled call request
     @param schedule_id: id of the schedule for the call request
     @type  schedule_id: str
     """
     if isinstance(schedule_id, basestring):
         schedule_id = ObjectId(schedule_id)
     if ScheduledCall.get_collection().find_one(schedule_id) is None:
         raise pulp_exceptions.MissingResource(schedule=str(schedule_id))
     scheduled_call_collection = ScheduledCall.get_collection()
     scheduled_call_collection.remove({'_id': schedule_id}, safe=True)
Example #36
0
    def test_with_past_runs(self, mock_time):
        # setup an hourly call that first ran not quite 2 hours ago, ran again
        # less than one hour ago, and should be scheduled to run at the end of
        # this hour
        mock_time.return_value = 1389389758.547976
        call = ScheduledCall('2014-01-10T20:00Z/PT1H', 'pulp.tasks.dosomething',
                             total_run_count=2, last_run_at='2014-01-10T21:00Z')

        next_run = call.calculate_next_run()

        self.assertEqual(dateutils.parse_iso8601_datetime('2014-01-10T22:00Z'),
                         dateutils.parse_iso8601_datetime(next_run))
Example #37
0
    def test_past_runs_not_due(self, mock_time):
        mock_time.return_value = 1389389758  # 2014-01-10T21:35:58
        # This call ran at the top of the hour, so it does not need to run again
        # until the top of the next hour.
        call = ScheduledCall('2014-01-10T20:00Z/PT1H', 'pulp.tasks.dosomething',
                             last_run_at='2014-01-10T21:00Z', total_run_count=2)
        entry = call.as_schedule_entry()

        is_due, seconds = entry.is_due()

        self.assertFalse(is_due)
        # this was hand-calculated as the remaining time until the next hourly run
        self.assertEqual(seconds, 1442)
Example #38
0
    def test_past_runs_due(self, mock_time):
        mock_time.return_value = 1389389758  # 2014-01-10T21:35:58
        # This call did not run at the top of the hour, so it is overdue and should
        # run now. Its next run will be back on the normal hourly schedule, at
        # the top of the next hour.
        call = ScheduledCall('2014-01-10T20:00Z/PT1H', 'pulp.tasks.dosomething',
                             last_run_at='2014-01-10T20:00Z', total_run_count=1)
        entry = call.as_schedule_entry()

        is_due, seconds = entry.is_due()

        self.assertTrue(is_due)
        # this was hand-calculated as the remaining time until the next hourly run
        self.assertEqual(seconds, 1442)
Example #39
0
def reset_failure_count(schedule_id):
    """
    Reset the consecutive failure count on a schedule to 0, presumably because
    it ran successfully.

    :param schedule_id: ID of the schedule whose count should be reset
    :type  schedule_id: str
    """
    spec = {'_id': ObjectId(schedule_id)}
    delta = {'$set': {
        'consecutive_failures': 0,
        'last_updated': time.time(),
    }}
    ScheduledCall.get_collection().update(spec=spec, document=delta)
Example #40
0
File: repo.py Project: pcreech/pulp
    def create(cls, repo_id, importer_id, sync_options, schedule, failure_threshold=None, enabled=True):
        """
        Create a new sync schedule for a given repository using the given importer.

        :param repo_id:         unique ID for a repository
        :type  repo_id:         basestring
        :param importer_id:     unique ID for an importer
        :type  importer_id:     basestring
        :param sync_options:    dictionary that contains the key 'override_config',
                                whose value should be passed as the 'overrides'
                                parameter to the sync task. This wasn't originally
                                documented, so it isn't clear why overrides value
                                couldn't be passed directly.
        :type  sync_options:    dict
        :param schedule_data:   dictionary that contains the key 'schedule', whose
                                value is an ISO8601 string. This wasn't originally
                                documented, so it isn't clear why the string itself
                                couldn't have been passed directly.
        :type  schedule_data:   dict

        :return:    new schedule instance
        :rtype:     pulp.server.db.model.dispatch.ScheduledCall
        """
        # validate the input
        importer_controller.get_valid_importer(repo_id, importer_id)
        utils.validate_keys(sync_options, _SYNC_OPTION_KEYS)
        utils.validate_initial_schedule_options(schedule, failure_threshold, enabled)

        task = repo_controller.queue_sync_with_auto_publish.name
        args = [repo_id]
        kwargs = {"overrides": sync_options["override_config"]}
        resource = importer_controller.build_resource_tag(repo_id, importer_id)
        schedule = ScheduledCall(
            schedule,
            task,
            args=args,
            kwargs=kwargs,
            resource=resource,
            failure_threshold=failure_threshold,
            enabled=enabled,
        )
        schedule.save()
        try:
            importer_controller.get_valid_importer(repo_id, importer_id)
        except exceptions.MissingResource:
            # back out of this whole thing, since the importer disappeared
            utils.delete(schedule.id)
            raise

        return schedule
Example #41
0
File: repo.py Project: pcreech/pulp
    def create(cls, repo_id, distributor_id, publish_options, schedule, failure_threshold=None, enabled=True):
        """
        Create a new scheduled publish for the given repository and distributor.

        :param repo_id:         unique ID for a repository
        :type  repo_id:         basestring
        :param distributor_id:  unique ID for a distributor
        :type  distributor_id:  basestring
        :param publish_options: dictionary that contains the key 'override_config',
                                whose value should be passed as the 'overrides'
                                parameter to the publish task. This wasn't originally
                                documented, so it isn't clear why overrides value
                                couldn't be passed directly.
        :type  sync_options:    dict
        :param schedule_data:   dictionary that contains the key 'schedule', whose
                                value is an ISO8601 string. This wasn't originally
                                documented, so it isn't clear why the string itself
                                couldn't have been passed directly.
        :type  schedule_data:   dict

        :return:    new schedule instance
        :rtype:     pulp.server.db.model.dispatch.ScheduledCall
        """
        dist = model.Distributor.objects.get_or_404(repo_id=repo_id, distributor_id=distributor_id)
        utils.validate_keys(publish_options, _PUBLISH_OPTION_KEYS)
        utils.validate_initial_schedule_options(schedule, failure_threshold, enabled)

        task = repo_controller.queue_publish.name
        args = [repo_id, distributor_id]
        kwargs = {"overrides": publish_options["override_config"]}
        schedule = ScheduledCall(
            schedule,
            task,
            args=args,
            kwargs=kwargs,
            resource=dist.resource_tag,
            failure_threshold=failure_threshold,
            enabled=enabled,
        )
        schedule.save()

        try:
            model.Distributor.objects.get_or_404(repo_id=repo_id, distributor_id=distributor_id)
        except exceptions.MissingResource:
            # back out of this whole thing, since the distributor disappeared
            utils.delete(schedule.id)
            raise

        return schedule
Example #42
0
 def add(self, call_request, schedule, **schedule_options):
     """
     Add a scheduled call request
     Valid schedule options:
      * failure_threshold: max number of consecutive failures, before scheduled call is disabled, None means no max
      * last_run: datetime of the last run of the call request or None if no last run
      * enabled: boolean flag if the scheduled call is enabled or not
     @param call_request: call request to schedule
     @type  call_request: pulp.server.dispatch.call.CallRequest
     @param schedule: iso8601 formatted interval schedule
     @type  schedule: str
     @param schedule_options: keyword options for this schedule
     @type  schedule_options: dict
     @return: schedule id if successfully scheduled or None otherwise
     @rtype:  str or None
     """
     validate_schedule_options(schedule, schedule_options)
     scheduled_call = ScheduledCall(call_request, schedule, **schedule_options)
     next_run = self.calculate_next_run(scheduled_call)
     if next_run is None:
         return None
     scheduled_call_collection = ScheduledCall.get_collection()
     scheduled_call['next_run'] = next_run
     scheduled_call_collection.insert(scheduled_call, safe=True)
     return str(scheduled_call['_id'])