def test_updated_scheduled_next_run(self): call_request = CallRequest(itinerary_call) interval = datetime.timedelta(minutes=2) now = datetime.datetime.now(tz=dateutils.utc_tz()) old_schedule = dateutils.format_iso8601_interval(interval, now) scheduled_id = self.scheduler.add(call_request, old_schedule) self.assertNotEqual(scheduled_id, None) scheduled_call = self.scheduled_call_collection.find_one({'_id': ObjectId(scheduled_id)}) self.assertNotEqual(scheduled_call, None) old_interval, start_time = dateutils.parse_iso8601_interval(old_schedule)[:2] start_time = dateutils.to_naive_utc_datetime(start_time) self.assertEqual(scheduled_call['last_run'], None) self.assertEqual(scheduled_call['first_run'], start_time + old_interval) self.assertEqual(scheduled_call['next_run'], start_time + old_interval) interval = datetime.timedelta(minutes=1) new_schedule = dateutils.format_iso8601_interval(interval, now) self.scheduler.update(scheduled_id, schedule=new_schedule) updated_scheduled_call = self.scheduled_call_collection.find_one({'_id': ObjectId(scheduled_id)}) new_interval = dateutils.parse_iso8601_interval(new_schedule)[0] self.assertEqual(updated_scheduled_call['last_run'], None) self.assertEqual(updated_scheduled_call['first_run'], start_time + old_interval) self.assertEqual(updated_scheduled_call['next_run'], start_time + new_interval)
def test_interval_start_time(self): d = datetime.timedelta(minutes=2) t = datetime.datetime(year=2014, month=11, day=5, hour=0, minute=23) s = dateutils.format_iso8601_interval(d, t) i, e, r = dateutils.parse_iso8601_interval(s) self.assertEqual(d, i) self.assertEqual(t, e)
def test_interval_recurrences(self): d = datetime.timedelta(hours=4, minutes=2, seconds=59) c = 4 s = dateutils.format_iso8601_interval(d, recurrences=c) i, t, r = dateutils.parse_iso8601_interval(s) self.assertEqual(d, i) self.assertEqual(c, r)
def test_interval_full(self): i1 = datetime.timedelta(hours=100) t1 = datetime.datetime(year=2, month=6, day=20, hour=2, minute=22, second=46) r1 = 5 s = dateutils.format_iso8601_interval(i1, t1, r1) i2, t2, r2 = dateutils.parse_iso8601_interval(s) self.assertEqual(i1, i2) self.assertEqual(t1, t2) self.assertEqual(r1, r2)
def test_calculate_next_run(self): call_request = CallRequest(call) interval = datetime.timedelta(minutes=1) schedule = dateutils.format_iso8601_interval(interval) scheduled_id = self.scheduler.add(call_request, schedule) scheduled_call = self.scheduled_call_collection.find_one({'_id': ObjectId(scheduled_id)}) next_run = self.scheduler.calculate_next_run(scheduled_call) self.assertFalse(next_run is None) self.assertTrue(next_run == scheduled_call['first_run']) self.scheduler.update_last_run(scheduled_call) updated_scheduled_call = self.scheduled_call_collection.find_one({'_id': ObjectId(scheduled_id)}) updated_next_run = self.scheduler.calculate_next_run(updated_scheduled_call) self.assertTrue(updated_next_run == updated_scheduled_call['last_run'])
def test_calculate_next_run_duration(self): call_request = CallRequest(call) now = datetime.datetime.now() interval = isodate.Duration(months=1) schedule = dateutils.format_iso8601_interval(interval, now) schedule_id = self.scheduler.add(call_request, schedule) scheduled_call = self.scheduled_call_collection.find_one({'_id': ObjectId(schedule_id)}) next_run = self.scheduler.calculate_next_run(scheduled_call) self.assertFalse(next_run is None) self.assertTrue(next_run == scheduled_call['first_run']) self.scheduler.update_last_run(scheduled_call) updated_scheduled_call = self.scheduled_call_collection.find_one({'_id': ObjectId(schedule_id)}) updated_next_run = self.scheduler.calculate_next_run(updated_scheduled_call) self.assertTrue(updated_next_run == updated_scheduled_call['last_run'])
def test_next_run_updated(self): call_request = CallRequest(itinerary_call) now = datetime.datetime.now() interval = datetime.timedelta(minutes=1) schedule = dateutils.format_iso8601_interval(interval, now) schedule_id = self.scheduler.add(call_request, schedule) scheduled_call = self.scheduled_call_collection.find_one({'_id': ObjectId(schedule_id)}) next_next_run = self.scheduler.calculate_next_run(scheduled_call) self.scheduler._get_scheduled_call_groups() updated_scheduled_call = self.scheduled_call_collection.find_one({'_id': ObjectId(schedule_id)}) self.assertEqual(next_next_run, updated_scheduled_call['next_run'])
def test_scheduled_collision(self): call_request_scheduled = CallRequest(itinerary_call) schedule = dateutils.format_iso8601_interval(datetime.timedelta(minutes=1), datetime.datetime.now()) schedule_id = self.scheduler.add(call_request_scheduled, schedule) call_request_in_progress = CallRequest(dummy_call) call_report_in_progress = CallReport.from_call_request(call_request_in_progress) call_report_in_progress.schedule_id = schedule_id # return a call report list out of the coordinator that has tasks from # this schedule in it # this will be cleaned up by the base class tearDown method mocked_coordinator = mock.Mock() mocked_call_reports = mock.Mock(return_value=[call_report_in_progress]) mocked_coordinator.find_call_reports = mocked_call_reports dispatch_factory.coordinator = mock.Mock(return_value=mocked_coordinator) call_group_generator = self.scheduler._get_call_request_groups_for_scheduled_itineraries() # call reports should have indicated a collision, in which we do not # run the scheduled call group again, indicated here by an "empty" # generator self.assertRaises(StopIteration, next, call_group_generator)
def test_interval(self): d = datetime.timedelta(hours=1) s = dateutils.format_iso8601_interval(d) i, t, r = dateutils.parse_iso8601_interval(s) self.assertTrue(d == i)
def rebalance_sync_schedule(errors=None): repoapi = RepositoryAPI() repos = get_repos() # get a list of sync frequencies syncgroups = dict() # dict of sync time -> [groups] default = None for ckey, sync in config.list(filter=dict(name__startswith="sync_frequency_")).items(): group = ckey.replace("sync_frequency_", "") if sync is None: logger.error("Sync frequency for %s is None, skipping" % group) continue synctime = 60 * 60 * int(sync) if "group" == "default": default = synctime else: try: syncgroups[synctime].append(group) except KeyError: syncgroups[synctime] = [group] # divide the repos up by sync time and sort them by inheritance, # reversed, to ensure that children get synced before parents and # a package doesn't just go straight to the final child cycles = dict() # dict of repo -> sync time for repo in repos.values(): cycles[repo["id"]] = default for synctime, groups in syncgroups.items(): if set(groups) & set(repo["groupid"]) and (cycles[repo["id"]] is None or synctime > cycles[repo["id"]]): cycles[repo["id"]] = synctime # finally, build a dict of sync time -> [repos] syncs = dict() for repoid, synctime in cycles.items(): if synctime is None: continue try: syncs[synctime].append(repos[repoid]) except KeyError: syncs[synctime] = [repos[repoid]] for synctime, syncrepos in syncs.items(): syncrepos = sort_repos_by_ancestry(syncrepos) syncrepos.reverse() # we count the total number of packages in all repos, and # divide them evenly amongst the timespan allotted. It's # worth noting that we count clones just the same as we count # "regular" repos, because it's createrepo, not the sync, that # really takes a lot of time and memory. pkgs = 0 for repo in syncrepos: if repo["package_count"] < 10: # we still have to run createrepo even if there are # very few (or no!) packages, so count very small # repos as 10 packages pkgs += 10 else: pkgs += repo["package_count"] try: pkgtime = float(synctime) / pkgs except ZeroDivisionError: pkgtime = 1 logger.debug("Allowing %s seconds per package" % pkgtime) # find tomorrow morning at 12:00 am tomorrow = datetime.datetime.today() + datetime.timedelta(days=1) start = datetime.datetime(tomorrow.year, tomorrow.month, tomorrow.day) if errors is None: errors = [] for repo in syncrepos: iso8601_start = format_iso8601_datetime(start) iso8601_interval = format_iso8601_interval(datetime.timedelta(seconds=synctime)) logger.debug("Scheduling %s to start at %s, sync every %s" % (repo["id"], iso8601_start, iso8601_interval)) schedule = parse_interval_schedule(iso8601_interval, iso8601_start, None) try: repoapi.change_sync_schedule(repo["id"], dict(schedule=schedule, options=dict())) reload_repo(repo["id"]) except ServerRequestError, err: errors.append("Could not set schedule for %s: %s" % (repo["id"], err[1])) start += datetime.timedelta(seconds=int(pkgtime * repo["package_count"]))