def renewal_event_is_fired_after_one_month(context): # after 1 month , AllocationSourceSnapshot is first updated. report_start_date = context.ts report_end_date = context.ts + timedelta(days=30) query = EventTable.objects.filter(name='allocation_source_renewed', payload__name__exact='DefaultAllocationSource') assert len(query) == 0 source_snapshot = AllocationSourceSnapshot.objects.filter(allocation_source=context.allocation_source_2).order_by( 'updated').last() assert total_usage(context.amit.user.username, report_start_date, allocation_source_name=context.allocation_source_2.name, end_date=report_end_date) == 72.0 source_snapshot.compute_used = total_usage(context.amit.user.username, report_start_date, allocation_source_name=context.allocation_source_2.name, end_date=report_end_date) source_snapshot.updated = context.ts + timedelta(days=29) source_snapshot.save() assert AllocationSourceSnapshot.objects.filter(allocation_source=context.allocation_source_2).order_by( 'updated').last().compute_used == 72.0 # rules engine is explicitly run current_time = context.ts + timedelta(days=30) run_all(rule_list=cyverse_rules, defined_variables=CyverseTestRenewalVariables(context.allocation_source_2, current_time), defined_actions=CyverseTestRenewalActions(context.allocation_source_2, current_time), ) query = EventTable.objects.filter(name='allocation_source_renewed', payload__name__exact='DefaultAllocationSource') assert len(query) == 1
def total_usage_is_five_hours(context): report_start_date = context.ts report_end_date = context.ts + timedelta(hours=4) tots = total_usage(context.julian.user.username, report_start_date, allocation_source_name=context.allocation_source_1.name, end_date=report_end_date) + total_usage(context.amit.user.username, report_start_date, allocation_source_name=context.allocation_source_1.name, end_date=report_end_date) assert tots == 5.0
def total_usage_is_zero(context): report_start_date = context.ts report_end_date = context.ts + timedelta(minutes=120) tots = total_usage(context.amit.user.username, report_start_date, allocation_source_name=context.allocation_source_1.name, end_date=report_end_date) print(tots) assert total_usage(context.amit.user.username, report_start_date, allocation_source_name=context.allocation_source_1.name, end_date=report_end_date) == 0.0
def step_impl(context): current_time = context.current_time for row in context.table: allocation_source = AllocationSource.objects.filter( name=context.allocation_sources_name[row['allocation_source_id']]).last() start_date = current_time if str(row['report start date']) == 'current' else parse( str(row['report start date'])) end_date = start_date + timedelta(days=int(row['number of days'])) celery_iterator = list(rrule(HOURLY, interval=12, dtstart=start_date, until=end_date)) prev_time = '' for current_time in celery_iterator: # update AllocationSourceSnapshot with the current compute_used if prev_time: update_snapshot_cyverse(end_date=current_time) prev_time = current_time context.time_at_the_end_of_calculation_check = current_time compute_used_total = 0 for user in allocation_source.all_users: compute_used_total += total_usage(user.username, start_date=start_date, end_date=end_date, allocation_source_name=context.allocation_sources_name[ row['allocation_source_id']]) compute_allowed = AllocationSourceSnapshot.objects.filter( allocation_source=allocation_source).last().compute_allowed compute_used_from_snapshot = AllocationSourceSnapshot.objects.filter( allocation_source=allocation_source).last().compute_used assert float(row['total compute used']) == float(compute_used_from_snapshot) assert float(row['current compute used']) == float(compute_used_from_snapshot) assert float(row['current compute allowed']) == float(compute_allowed)
def get_compute_used(allocation_source, current_time, prev_time): compute_used = 0 for user in allocation_source.all_users: compute_used += total_usage(user.username, start_date=prev_time, end_date=current_time, allocation_source_name=allocation_source.name) return compute_used
def calculate_allocations_used_by_allocation_source_after_certain_number_of_days(context): current_time = context.current_time for row in context.table: allocation_source = AllocationSource.objects.filter( name=context.allocation_sources_name[row['allocation_source_id']]).last() start_date = current_time if str(row['report start date']) == 'current' else parse( str(row['report start date'])) end_date = start_date + timedelta(days=int(row['number of days'])) celery_iterator = list(rrule(HOURLY, interval=12, dtstart=start_date, until=end_date)) prev_time = '' for current_time in celery_iterator: # update AllocationSourceSnapshot with the current compute_used if prev_time: update_snapshot_cyverse(end_date=current_time) prev_time = current_time context.time_at_the_end_of_calculation_check = current_time compute_used_total = 0 for user in allocation_source.all_users: compute_used_total += total_usage(user.username, start_date=start_date, end_date=end_date, allocation_source_name=context.allocation_sources_name[ row['allocation_source_id']]) compute_allowed = AllocationSourceSnapshot.objects.filter( allocation_source=allocation_source).last().compute_allowed compute_used_from_snapshot = AllocationSourceSnapshot.objects.filter( allocation_source=allocation_source).last().compute_used assert float(row['total compute used']) == float(compute_used_from_snapshot) assert float(row['current compute used']) == float(compute_used_from_snapshot) assert float(row['current compute allowed']) == float(compute_allowed)
def get_compute_used(allocation_source, current_time, prev_time): compute_used = 0 for user in allocation_source.all_users: compute_used += total_usage(user.username, start_date=prev_time, end_date=current_time, allocation_source_name=allocation_source.name) return compute_used
def step_impl(context): report_start_date = context.ts report_end_date = context.ts + timedelta(hours=4) assert total_usage(context.amit.user.username, report_start_date, allocation_source_name=context.allocation_source_1.name, end_date=report_end_date) == 2.0
def update_snapshot_cyverse(start_date=None, end_date=None): logger.debug("update_snapshot_cyverse task started at %s." % datetime.now()) end_date = timezone.now().replace( microsecond=0) if not end_date else end_date for allocation_source in AllocationSource.objects.order_by('name'): # calculate and save snapshots here allocation_source_name = allocation_source.name last_renewal_event = EventTable.objects.filter( name='allocation_source_created_or_renewed', payload__allocation_source_name__exact=str( allocation_source_name)).order_by('timestamp') if not last_renewal_event: logger.info('Allocation Source %s Create/Renewal event missing', allocation_source_name) continue start_date = last_renewal_event.last().timestamp.replace( microsecond=0) if not start_date else start_date total_compute_used = 0 total_burn_rate = 0 for user in allocation_source.all_users: compute_used, burn_rate = total_usage( user.username, start_date=start_date, end_date=end_date, allocation_source_name=allocation_source_name, burn_rate=True) UserAllocationSnapshot.objects.update_or_create( allocation_source=allocation_source, user=user, defaults={ 'compute_used': compute_used, 'burn_rate': burn_rate }) total_compute_used += compute_used total_burn_rate += burn_rate AllocationSourceSnapshot.objects.update_or_create( allocation_source=allocation_source, defaults={ 'compute_used': total_compute_used, 'global_burn_rate': total_burn_rate }) run_all( rule_list=cyverse_rules, defined_variables=CyverseTestRenewalVariables( allocation_source, end_date, start_date), defined_actions=CyverseTestRenewalActions(allocation_source, end_date), ) # At the end of the task, fire-off an allocation threshold check logger.debug("update_snapshot_cyverse task finished at %s." % datetime.now()) allocation_threshold_check.apply_async()
def update_snapshot(start_date=None, end_date=None): end_date = end_date or timezone.now() # TODO: Read this start_date from last 'reset event' for each allocation source start_date = start_date or '2016-09-01 00:00:00.0-05' tas_api_obj = TASAPIDriver() allocation_source_usage_from_tas = tas_api_obj.get_all_projects() for project in allocation_source_usage_from_tas: total_burn_rate = 0 allocation_source_name = project['chargeCode'] try: allocation_source = AllocationSource.objects.filter( name=allocation_source_name ).order_by('id').last() if not allocation_source: continue created_or_updated_event = EventTable.objects.filter( name='allocation_source_created_or_renewed', payload__allocation_source_name=allocation_source.name ).order_by('timestamp').last() if created_or_updated_event: # if renewed, change ignore old allocation usage start_date = created_or_updated_event.payload['start_date'] for user in allocation_source.all_users: compute_used, burn_rate = total_usage( user.username, start_date, allocation_source_name=allocation_source.name, end_date=end_date, burn_rate=True ) total_burn_rate += burn_rate UserAllocationSnapshot.objects.update_or_create( allocation_source_id=allocation_source.id, user_id=user.id, defaults={ 'compute_used': compute_used, 'burn_rate': burn_rate } ) except KeyError: # This allocation source does not exist in our database yet. Create it? Skip for now. continue valid_allocation = select_valid_allocation(project['allocations']) compute_used = valid_allocation['computeUsed'] if valid_allocation else 0 AllocationSourceSnapshot.objects.update_or_create( allocation_source_id=allocation_source.id, defaults={ 'compute_used': compute_used, 'global_burn_rate': total_burn_rate } ) return True
def total_user_is_two_hours(context): report_start_date = context.ts report_end_date = context.ts + timedelta(hours=4) assert total_usage( context.amit.user.username, report_start_date, allocation_source_name=context.allocation_source_1.name, end_date=report_end_date ) == 2.0
def test_total_usage(self): ts = parse('2016-10-04T00:00+00:00') allocation_source = create_allocation_source(name='TestSource', compute_allowed=1000,timestamp=ts) # In this workflow the instance_allocation_source_changed event is fired before any instance status history is created. Hence the instance will report usage as 0.0 because # it thinks the allocation source is 'N/A'. A fix for this would be to decouple instance status histories and events completely in the allocation logic. workflow1 = UserWorkflow() workflow1.assign_allocation_source_to_user(allocation_source, timestamp=ts + timedelta(minutes=10)) instance = workflow1.create_instance(start_date=ts+timedelta(minutes=30)) workflow1.create_instance_status_history(instance,start_date=ts+timedelta(days=30),status='suspend') workflow1.assign_allocation_source_to_instance(allocation_source, instance, timestamp=ts + timedelta(minutes=40)) report_start_date = ts report_end_date=ts + timedelta(minutes=160) self.assertEqual( total_usage(workflow1.user.username,report_start_date,allocation_source_name=allocation_source.name,end_date=report_end_date), 2.0) workflow2 = UserWorkflow() workflow2.assign_allocation_source_to_user(allocation_source, timestamp=ts + timedelta(minutes=10)) instance = workflow2.create_instance(start_date=ts + timedelta(minutes=30)) workflow2.assign_allocation_source_to_instance(allocation_source, instance, timestamp=ts + timedelta(minutes=40)) ish_start_date=ts+timedelta(minutes=120) workflow2.create_instance_status_history(instance, start_date=ish_start_date,status='suspended') report_start_date = ts report_end_date = ts+timedelta(minutes=120) self.assertEqual( total_usage(workflow2.user.username, report_start_date, allocation_source_name=allocation_source.name, end_date=report_end_date), 1.33) # check total allocation usage so far end_date = ts+timedelta(days=30) update_snapshot_cyverse_allocation(ts,end_date) self.assertEqual( float(allocation_source.compute_used) , 720.66 )
def update_snapshot(): if not settings.USE_ALLOCATION_SOURCE: return False allocation_source_total_compute = {} allocation_source_total_burn_rate = {} end_date = timezone.now() for source in AllocationSource.objects.all(): # iterate over user + allocation_source combo for user_allocation_source in UserAllocationSource.objects.filter( allocation_source__exact=source.id): user = user_allocation_source.user # determine end date and start date using last snapshot start_date = user.date_joined # calculate compute used and burn rate for the user and allocation source combo compute_used, burn_rate = total_usage( user.username, start_date, allocation_source_name=source.name, end_date=end_date, burn_rate=True) allocation_source_total_compute[ source.name] = allocation_source_total_compute.get( source.name, 0) + compute_used allocation_source_total_burn_rate[ source.name] = allocation_source_total_burn_rate.get( source.name, 0) + burn_rate payload_ubr = { "allocation_source_id": source.source_id, "username": user.username, "burn_rate": burn_rate, "compute_used": compute_used } EventTable.create_event("user_allocation_snapshot_changed", payload_ubr, user.username) payload_as = { "allocation_source_id": source.source_id, "compute_used": allocation_source_total_compute.get(source.name, 0), "global_burn_rate": allocation_source_total_burn_rate.get(source.name, 0) } EventTable.create_event("allocation_source_snapshot", payload_as, source.name) return True
def _create_tas_report_for(user, tacc_username, tacc_project_name, end_date): """ Create a new report """ if not end_date: raise TASPluginException("Explicit end date required") if not user: raise TASPluginException("User missing") if not tacc_username: raise TASPluginException("TACC Username missing") if not tacc_project_name: raise TASPluginException("OpenStack/TACC Project missing") last_report = TASAllocationReport.objects.filter( project_name=tacc_project_name, user=user ).order_by('end_date').last() if not last_report: start_date = user.date_joined else: start_date = last_report.end_date compute_used = total_usage( user.username, start_date, allocation_source_name=tacc_project_name, end_date=end_date ) if compute_used < 0: raise TASPluginException( "Compute usage was not accurately calculated for user:%s for start_date:%s and end_date:%s" % (user, start_date, end_date) ) new_report = TASAllocationReport.objects.create( user=user, username=tacc_username, project_name=tacc_project_name, compute_used=compute_used, start_date=start_date, end_date=end_date, tacc_api=settings.TACC_API_URL ) logger.info("Created New Report:%s" % new_report) return new_report
def _create_tas_report_for(user, tacc_username, tacc_project_name, end_date): """ Create a new report """ if not end_date: raise TASPluginException("Explicit end date required") if not user: raise TASPluginException("User missing") if not tacc_username: raise TASPluginException("TACC Username missing") if not tacc_project_name: raise TASPluginException("OpenStack/TACC Project missing") last_report = TASAllocationReport.objects.filter( project_name=tacc_project_name, user=user ).order_by('end_date').last() if not last_report: start_date = user.date_joined else: start_date = last_report.end_date compute_used = total_usage( user.username, start_date, allocation_source_name=tacc_project_name, end_date=end_date ) if compute_used < 0: raise TASPluginException( "Compute usage was not accurately calculated for user:%s" % user ) new_report = TASAllocationReport.objects.create( user=user, username=tacc_username, project_name=tacc_project_name, compute_used=compute_used, start_date=start_date, end_date=end_date, tacc_api=settings.TACC_API_URL ) logger.info("Created New Report:%s" % new_report) return new_report
def update_snapshot_cyverse(start_date=None, end_date=None): logger.debug("update_snapshot_cyverse task started at %s." % datetime.now()) end_date = timezone.now().replace(microsecond=0) if not end_date else end_date for allocation_source in AllocationSource.objects.order_by('name'): # calculate and save snapshots here allocation_source_name = allocation_source.name last_renewal_event = EventTable.objects.filter( name='allocation_source_created_or_renewed', payload__allocation_source_name__exact=str(allocation_source_name)).order_by('timestamp') if not last_renewal_event: logger.info('Allocation Source %s Create/Renewal event missing', allocation_source_name) continue start_date = last_renewal_event.last().timestamp.replace(microsecond=0) if not start_date else start_date total_compute_used = 0 total_burn_rate = 0 for user in allocation_source.all_users: compute_used, burn_rate = total_usage(user.username, start_date=start_date, end_date=end_date, allocation_source_name=allocation_source_name, burn_rate=True) UserAllocationSnapshot.objects.update_or_create(allocation_source=allocation_source, user=user, defaults={'compute_used': compute_used, 'burn_rate': burn_rate}) total_compute_used += compute_used total_burn_rate += burn_rate AllocationSourceSnapshot.objects.update_or_create(allocation_source=allocation_source, defaults={'compute_used': total_compute_used, 'global_burn_rate': total_burn_rate}) run_all(rule_list=cyverse_rules, defined_variables=CyverseTestRenewalVariables(allocation_source, current_time=end_date, last_renewal_event_date=start_date), defined_actions=CyverseTestRenewalActions(allocation_source, current_time=end_date)) # At the end of the task, fire-off an allocation threshold check logger.debug("update_snapshot_cyverse task finished at %s." % datetime.now()) allocation_threshold_check.apply_async()
def renewal_event_is_fired_twice(context): # after another 14 days , AllocationSourceSnapshot is updated. report_start_date = context.ts + timedelta(days=30) report_end_date = report_start_date + timedelta(days=43) query = EventTable.objects.filter(name='allocation_source_renewed', payload__name__exact='DefaultAllocationSource') assert len(query) == 1 source_snapshot = AllocationSourceSnapshot.objects.filter(allocation_source=context.allocation_source_2).order_by( 'updated').last() amit_usage = total_usage(context.amit.user.username, report_start_date, allocation_source_name=context.allocation_source_2.name, end_date=report_end_date) julian_usage = total_usage(context.julian.user.username, report_start_date, allocation_source_name=context.allocation_source_2.name, end_date=report_end_date) assert amit_usage == 120.0 assert julian_usage == 96.0 source_snapshot.compute_used = amit_usage + julian_usage source_snapshot.updated = context.ts + timedelta(days=43) source_snapshot.save() assert AllocationSourceSnapshot.objects.filter(allocation_source=context.allocation_source_2).order_by( 'updated').last().compute_used == 216.0 # rules engine is explicitly run current_time = context.ts + timedelta(days=44) run_all(rule_list=cyverse_rules, defined_variables=CyverseTestRenewalVariables(context.allocation_source_2, current_time), defined_actions=CyverseTestRenewalActions(context.allocation_source_2, current_time), ) assert EventTable.objects.filter(name='allocation_source_renewed') == 2 # after another 14 days , AllocationSourceSnapshot is updated. report_start_date = context.ts + timedelta(days=44) report_end_date = report_start_date + timedelta(days=57) query = EventTable.objects.filter(name='allocation_source_renewed', payload__name__exact='DefaultAllocationSource') source_snapshot = AllocationSourceSnapshot.objects.filter(allocation_source=context.allocation_source_2).order_by( 'updated').last() amit_usage = total_usage(context.amit.user.username, report_start_date, allocation_source_name=context.allocation_source_2.name, end_date=report_end_date) julian_usage = total_usage(context.julian.user.username, report_start_date, allocation_source_name=context.allocation_source_2.name, end_date=report_end_date) assert amit_usage == 0.0 assert julian_usage == 192.0 source_snapshot.compute_used = amit_usage + julian_usage source_snapshot.updated = context.ts + timedelta(days=57) source_snapshot.save() assert AllocationSourceSnapshot.objects.filter(allocation_source=context.allocation_source_2).order_by( 'updated').last().compute_used == 192.0 # rules engine is explicitly run current_time = context.ts + timedelta(days=58) run_all(rule_list=cyverse_rules, defined_variables=CyverseTestRenewalVariables(context.allocation_source_2, current_time), defined_actions=CyverseTestRenewalActions(context.allocation_source_2, current_time), ) assert EventTable.objects.filter(name='allocation_source_renewed') == 3
def test_total_usage(self): ts = parse('2016-10-04T00:00+00:00') allocation_source = create_allocation_source(name='TestSource', compute_allowed=1000, timestamp=ts) # In this workflow the instance_allocation_source_changed event is # fired before any instance status history is created. Hence the # instance will report usage as 0.0 because it thinks the allocation # source is 'N/A'. A fix for this would be to decouple instance status # histories and events completely in the allocation logic. workflow1 = UserWorkflow() workflow1.assign_allocation_source_to_user(allocation_source, timestamp=ts + timedelta(minutes=10)) instance = workflow1.create_instance(start_date=ts + timedelta(minutes=30)) workflow1.create_instance_status_history(instance, start_date=ts + timedelta(days=30), status='suspend') workflow1.assign_allocation_source_to_instance(allocation_source, instance, timestamp=ts + timedelta(minutes=40)) report_start_date = ts report_end_date = ts + timedelta(minutes=160) self.assertEqual( total_usage(workflow1.user.username, report_start_date, allocation_source_name=allocation_source.name, end_date=report_end_date), 2.0) workflow2 = UserWorkflow() workflow2.assign_allocation_source_to_user(allocation_source, timestamp=ts + timedelta(minutes=10)) instance = workflow2.create_instance(start_date=ts + timedelta(minutes=30)) workflow2.assign_allocation_source_to_instance(allocation_source, instance, timestamp=ts + timedelta(minutes=40)) ish_start_date = ts + timedelta(minutes=120) workflow2.create_instance_status_history(instance, start_date=ish_start_date, status='suspended') report_start_date = ts report_end_date = ts + timedelta(minutes=120) self.assertEqual( total_usage(workflow2.user.username, report_start_date, allocation_source_name=allocation_source.name, end_date=report_end_date), 1.33) # check total allocation usage so far end_date = ts + timedelta(days=30) update_snapshot_cyverse_allocation(ts, end_date) self.assertEqual(float(allocation_source.compute_used), 720.66)