Example #1
0
    def test_reschedule(self):
        """
        Test reschedule.
        """
        self.assertEqual(1, Run.objects.filter(job_id=1).count())
        Job.objects.get(pk=1).reschedule()
        # because of the pre-scheduling, reschedule should result in max
        # 2 scheduled runs.
        self.assertEqual(2, Run.objects.filter(job_id=1).count())
        Job.objects.get(pk=1).reschedule()
        self.assertEqual(2, Run.objects.filter(job_id=1).count())

        Run.objects.filter(pk=1).update(schedule_dts=timezone.now(),
                                        enqueue_dts=timezone.now(),
                                        return_dts=timezone.now())

        # we just completed one run, so reschedule should result in a new
        # schedule.
        Job.objects.get(pk=1).reschedule()
        self.assertEqual(3, Run.objects.filter(job_id=1).count())

        # make sure the last run is scheduled based on the schedule_dts of the
        # pre-last run
        runs = Run.objects.filter(job_id=1).all()
        self.assertEqual(
            correct_dst_difference(runs[1].schedule_dts,
                                   runs[1].schedule_dts + timedelta(days=1)),
            runs[2].schedule_dts)
Example #2
0
    def reschedule(self):
        """
        Reschedule job.

        This will check if the job is setup for reschedule. Then it will
        try to get the next reschedule date. When getting this date fails
        (eg: when the rescheduling always falls within the reschedule exclude),
        it will send out an e-mail to the Job-Runner admins and the e-mail
        addresses that are setup for this job, script and server.

        """
        # there is already an other run which is not finished yet, do
        # not re-schedule, it will be rescheduled when the other job
        # finishes
        active_runs = self.run_set.filter(return_dts__isnull=True)
        active_schedule_ids = []

        # TODO: is there a way in the Django ORM to achieve the same?
        for active_run in active_runs:
            if not active_run.schedule_id in active_schedule_ids:
                active_schedule_ids.append(active_run.schedule_id)

        # since we are pre-scheduling (a new run is created, before the
        # scheduled run is sent to the worker), having one schedule id is valid
        if len(active_schedule_ids) > 1:
            logger.error('Multiple ({0}) active schedule ids for {1}'.format(
                active_schedule_ids, self.log_name()))
            return

        logger.info('Attempting rescheduling {0}'.format(self.log_name()))

        # check if job is setup for re-scheduling
        if self.reschedule_interval_type and self.reschedule_interval:
            try:
                # order by -pk to get the last non-manual scheduled run
                # from which we need to increment
                last_run = self.run_set.filter(
                    is_manual=False).order_by('-pk')[0]
            except IndexError:
                logger.error('Reschedule failed for {0}: IndexError'.format(
                    self.log_name()))
                return

            try:
                reschedule_date = self._get_reschedule_date(
                    last_run.schedule_dts)

                # correct daylight saving-time changes to make sure we keep
                # re-scheduling at the same hour (in local time).
                reschedule_date = correct_dst_difference(
                    last_run.schedule_dts, reschedule_date)

                self.schedule(reschedule_date)
                logger.info('Rescheduled {0} for {1}'.format(
                    self.log_name(), reschedule_date))
            except RescheduleException:
                logger.error(
                    'Reschedule failed for {0}: RescheduleException'.format(
                        self.log_name()))
                notifications.reschedule_failed(self)
Example #3
0
    def test_reschedule_not_in_past(self):
        """
        Test reschedule dts is never in the past.
        """
        dts_now = timezone.now()

        Run.objects.filter(pk=1).update(
            schedule_dts=dts_now - timedelta(days=31),
            enqueue_dts=dts_now - timedelta(days=31),
            return_dts=dts_now - timedelta(days=31)
        )

        Job.objects.get(pk=1).reschedule()
        self.assertEqual(2, Run.objects.filter(job_id=1).count())

        run = Run.objects.get(pk=3)

        # we have to make sure to use correct_dst_difference since between
        # 31 days ago and tomorrow, we might have changed from or to DTS.
        self.assertEqual(
            run.schedule_dts,
            correct_dst_difference(
                dts_now - timedelta(days=31),
                dts_now + timedelta(days=1)
            )
        )
Example #4
0
    def test_reschedule(self):
        """
        Test reschedule.
        """
        self.assertEqual(1, Run.objects.filter(job_id=1).count())
        Job.objects.get(pk=1).reschedule()
        # because of the pre-scheduling, reschedule should result in max
        # 2 scheduled runs.
        self.assertEqual(2, Run.objects.filter(job_id=1).count())
        Job.objects.get(pk=1).reschedule()
        self.assertEqual(2, Run.objects.filter(job_id=1).count())

        Run.objects.filter(pk=1).update(
            schedule_dts=timezone.now(),
            enqueue_dts=timezone.now(),
            return_dts=timezone.now()
        )

        # we just completed one run, so reschedule should result in a new
        # schedule.
        Job.objects.get(pk=1).reschedule()
        self.assertEqual(3, Run.objects.filter(job_id=1).count())

        # make sure the last run is scheduled based on the schedule_dts of the
        # pre-last run
        runs = Run.objects.filter(job_id=1).all()
        self.assertEqual(
            correct_dst_difference(
                runs[1].schedule_dts,
                runs[1].schedule_dts + timedelta(days=1)
            ),
            runs[2].schedule_dts
        )
Example #5
0
    def test_correct_dts_difference(self):
        """
        Test :func:`.correct_dst_difference`.

        This will test that during a DST change (from +1 to +2), the
        :func:`.correct_dst_difference` function will make sure that the
        local will stay the same.

        """
        tzinfo = get_current_timezone()
        prev_dts = tzinfo.localize(datetime(2013, 3, 30, 4))
        next_dts = prev_dts + timedelta(days=1)
        expected_next_dts = tzinfo.localize(datetime(2013, 3, 31, 4))

        # test that indeed there is a DST change
        self.assertEqual(timedelta(hours=1), prev_dts.utcoffset())
        self.assertEqual(timedelta(hours=2), expected_next_dts.utcoffset())

        # test that normally, the job would be scheduled at 05:00
        self.assertEqual(
            tzinfo.localize(datetime(2013, 3, 31, 5)),
            next_dts
        )

        # test that correct_dst_difference is correcting this to 04:00
        self.assertEqual(
            expected_next_dts,
            correct_dst_difference(prev_dts, next_dts)
        )
Example #6
0
    def test_reschedule_monthly_after_schedule_dts(self):
        """
        Test monthy reschedule after schedule dts.
        """
        in_and_expected = [
            (datetime(2032, 1, 1), datetime(2032, 2, 1)),
            (datetime(2032, 2, 1), datetime(2032, 3, 1)),
            (datetime(2032, 3, 1), datetime(2032, 4, 1)),
            (datetime(2032, 4, 1), datetime(2032, 5, 1)),
            (datetime(2032, 6, 1), datetime(2032, 7, 1)),
            (datetime(2032, 12, 1), datetime(2033, 1, 1)),
        ]

        for in_dts, expected_dts in in_and_expected:

            # if you do not use utc as a timezone here, you get funny results
            # because of day light saving time switching
            in_dts = timezone.make_aware(
                in_dts, timezone.utc)
            expected_dts = timezone.make_aware(
                expected_dts, timezone.utc)

            job = Job.objects.get(pk=1)
            job.reschedule_interval_type = 'MONTH'
            job.save()

            job.run_set.all().delete()

            Run.objects.create(
                job=job,
                schedule_dts=in_dts,
                enqueue_dts=in_dts,
                start_dts=in_dts,
                return_dts=in_dts,
            )

            job.reschedule()
            run = Run.objects.filter(job=1, enqueue_dts__isnull=True)[0]
            self.assertEqual(
                correct_dst_difference(in_dts, expected_dts),
                run.schedule_dts
            )
Example #7
0
    def test_reschedule_not_in_past(self):
        """
        Test reschedule dts is never in the past.
        """
        dts_now = timezone.now()

        Run.objects.filter(pk=1).update(
            schedule_dts=dts_now - timedelta(days=31),
            enqueue_dts=dts_now - timedelta(days=31),
            return_dts=dts_now - timedelta(days=31))

        Job.objects.get(pk=1).reschedule()
        self.assertEqual(2, Run.objects.filter(job_id=1).count())

        run = Run.objects.get(pk=3)

        # we have to make sure to use correct_dst_difference since between
        # 31 days ago and tomorrow, we might have changed from or to DTS.
        self.assertEqual(
            run.schedule_dts,
            correct_dst_difference(dts_now - timedelta(days=31),
                                   dts_now + timedelta(days=1)))
Example #8
0
    def test_reschedule_monthly_after_schedule_dts(self):
        """
        Test monthy reschedule after schedule dts.
        """
        in_and_expected = [
            (datetime(2032, 1, 1), datetime(2032, 2, 1)),
            (datetime(2032, 2, 1), datetime(2032, 3, 1)),
            (datetime(2032, 3, 1), datetime(2032, 4, 1)),
            (datetime(2032, 4, 1), datetime(2032, 5, 1)),
            (datetime(2032, 6, 1), datetime(2032, 7, 1)),
            (datetime(2032, 12, 1), datetime(2033, 1, 1)),
        ]

        for in_dts, expected_dts in in_and_expected:

            # if you do not use utc as a timezone here, you get funny results
            # because of day light saving time switching
            in_dts = timezone.make_aware(in_dts, timezone.utc)
            expected_dts = timezone.make_aware(expected_dts, timezone.utc)

            job = Job.objects.get(pk=1)
            job.reschedule_interval_type = 'MONTH'
            job.save()

            job.run_set.all().delete()

            Run.objects.create(
                job=job,
                schedule_dts=in_dts,
                enqueue_dts=in_dts,
                start_dts=in_dts,
                return_dts=in_dts,
            )

            job.reschedule()
            run = Run.objects.filter(job=1, enqueue_dts__isnull=True)[0]
            self.assertEqual(correct_dst_difference(in_dts, expected_dts),
                             run.schedule_dts)
Example #9
0
    def test_correct_dts_difference(self):
        """
        Test :func:`.correct_dst_difference`.

        This will test that during a DST change (from +1 to +2), the
        :func:`.correct_dst_difference` function will make sure that the
        local will stay the same.

        """
        tzinfo = get_current_timezone()
        prev_dts = tzinfo.localize(datetime(2013, 3, 30, 4))
        next_dts = prev_dts + timedelta(days=1)
        expected_next_dts = tzinfo.localize(datetime(2013, 3, 31, 4))

        # test that indeed there is a DST change
        self.assertEqual(timedelta(hours=1), prev_dts.utcoffset())
        self.assertEqual(timedelta(hours=2), expected_next_dts.utcoffset())

        # test that normally, the job would be scheduled at 05:00
        self.assertEqual(tzinfo.localize(datetime(2013, 3, 31, 5)), next_dts)

        # test that correct_dst_difference is correcting this to 04:00
        self.assertEqual(expected_next_dts,
                         correct_dst_difference(prev_dts, next_dts))