def test_instance_already_power_off(self, mock_aws, mock_calculate_max_concurrent): """Don't create power_off for instance that already has recent power_off.""" event = account_helper.generate_single_instance_event( self.instance, self.power_off_time, event_type=InstanceEvent.TYPE.power_off) self.process_event(event) mock_calculate_max_concurrent.reset_mock() described_instances = { self.region: [ util_helper.generate_dummy_describe_instance( instance_id=self.aws_instance.ec2_instance_id, state=aws.InstanceState.stopped, ) ] } mock_aws.describe_instances_everywhere.return_value = described_instances describe_time = self.power_off_time + datetime.timedelta(days=1) with util_helper.clouditardis(describe_time): tasks.initial_aws_describe_instances(self.account.id) mock_calculate_max_concurrent.assert_not_called() self.assertExpectedEventsAndRun()
def test_process_instance_event_recalculate_runs( self, mock_schedule_concurrent_task): """ Test that we recalculate runs when new instance events occur. Initial Runs (2,-): [ #------------] New power off event at (,13) results in the run being updated (2,13): [ ############ ] """ # Calculate the runs directly instead of scheduling a task for testing purposes mock_schedule_concurrent_task.side_effect = calculate_max_concurrent_usage instance = api_helper.generate_instance(self.account) started_at = util_helper.utc_dt(2018, 1, 2, 0, 0, 0) start_event = api_helper.generate_single_instance_event( instance, occurred_at=started_at, event_type=InstanceEvent.TYPE.power_on, no_instance_type=True, ) tasks.process_instance_event(start_event) occurred_at = util_helper.utc_dt(2018, 1, 13, 0, 0, 0) instance_event = api_helper.generate_single_instance_event( instance=instance, occurred_at=occurred_at, event_type=InstanceEvent.TYPE.power_off, no_instance_type=True, ) tasks.process_instance_event(instance_event) runs = list(Run.objects.all()) self.assertEqual(1, len(runs)) self.assertEqual(started_at, runs[0].start_time) self.assertEqual(occurred_at, runs[0].end_time)
def setUp(self): """Set up data for an account with a running instance.""" self.account = account_helper.generate_cloud_account() self.instance = account_helper.generate_instance(self.account) self.aws_instance = self.instance.content_object self.region = self.aws_instance.region self.power_on_time = util_helper.utc_dt(2018, 1, 1, 0, 0, 0) self.power_off_time = util_helper.utc_dt(2019, 1, 1, 0, 0, 0) event = account_helper.generate_single_instance_event( self.instance, self.power_on_time, event_type=InstanceEvent.TYPE.power_on) self.process_event(event)
def test_delete_account_with_instance_event_succeeds( self, mock_notify_sources): """ Test that the account deleted succeeds when account has power_on instance event. When account is deleted, we call disable() on the account with the power_off_instances flag set to false. This prevents the disable from attempting to create an instance event in the same transaction as the delete, since doing so will fail. """ image = helper.generate_image() instance = helper.generate_instance(cloud_account=self.account, image=image) helper.generate_single_instance_event( instance=instance, occurred_at=util_helper.utc_dt(2019, 1, 1, 0, 0, 0), event_type=models.InstanceEvent.TYPE.power_on, ) with patch("api.clouds.aws.util.delete_cloudtrail" ) as mock_delete_cloudtrail: mock_delete_cloudtrail.return_value = True self.account.delete() self.assertEqual(0, aws_models.AwsCloudAccount.objects.count())
def test_process_instance_event_new_run(self, mock_recalculate_runs, mock_schedule_concurrent_task): """ Test new run is created if it occurred after all runs and is power on. account.util.recalculate_runs should not be ran in this case. Initial Runs (2,5): [ #### ] New power on event at (10,) results in 2 runs (2,5) (10,-): [ #### #-----] """ # Calculate the runs directly instead of scheduling a task for testing purposes mock_schedule_concurrent_task.side_effect = calculate_max_concurrent_usage instance = api_helper.generate_instance(self.account) run_time = ( util_helper.utc_dt(2018, 1, 2, 0, 0, 0), util_helper.utc_dt(2018, 1, 5, 0, 0, 0), ) api_helper.generate_single_run(instance, run_time) occurred_at = util_helper.utc_dt(2018, 1, 10, 0, 0, 0) instance_event = api_helper.generate_single_instance_event( instance=instance, occurred_at=occurred_at, event_type=InstanceEvent.TYPE.power_on, instance_type=None, ) tasks.process_instance_event(instance_event) runs = list(Run.objects.all()) self.assertEqual(2, len(runs)) # Since we're adding a new run, recalculate_runs shouldn't be called mock_recalculate_runs.assert_not_called()
def test_process_instance_event_power_off(self, mock_recalculate_runs): """ Test new run is not if a power off event occurs after all runs. account.util.recalculate_runs should not be ran in this case. Initial Runs (2,5): [ #### ] New power off event at (10,) results in 1 runs (2,5): [ #### ] """ instance = api_helper.generate_instance(self.account) run_time = ( util_helper.utc_dt(2018, 1, 2, 0, 0, 0), util_helper.utc_dt(2018, 1, 5, 0, 0, 0), ) api_helper.generate_single_run(instance, run_time) occurred_at = util_helper.utc_dt(2018, 1, 10, 0, 0, 0) instance_event = api_helper.generate_single_instance_event( instance=instance, occurred_at=occurred_at, event_type=InstanceEvent.TYPE.power_off, instance_type=None, ) tasks.process_instance_event(instance_event) runs = list(Run.objects.all()) self.assertEqual(1, len(runs)) mock_recalculate_runs.assert_not_called()
def create_events_for_instance( self, instance, since, now, seconds, max_run_count, max_secs_between_runs, min_run_secs, max_run_secs, runs_counts, cloud_type, ): """ Create random InstanceEvents for a specific Instance. Returns: int number of events created """ # Find an initial random time between the start and now. start_time = since + datetime.timedelta(seconds=random.randrange(0, seconds)) runs_to_make = int( min( random.gauss(max_run_count / 2 + 0.5, max_run_count / 5), max_run_count, ) ) runs_made = 0 events_count = 0 for __ in range(runs_to_make): # Always create the first "on" event. account_helper.generate_single_instance_event( instance=instance, occurred_at=start_time, event_type=InstanceEvent.TYPE.power_on, cloud_type=cloud_type, ) events_count += 1 runs_made += 1 run_duration = random.uniform(min_run_secs, max_run_secs) stop_time = start_time + datetime.timedelta(seconds=run_duration) if stop_time > now: # Don't allow creating events in the future. break account_helper.generate_single_instance_event( instance=instance, occurred_at=stop_time, event_type=InstanceEvent.TYPE.power_off, cloud_type=cloud_type, ) events_count += 1 # Find a new starting time for the next run. delay = random.randrange(0, max_secs_between_runs) + 1 start_time = stop_time + datetime.timedelta(seconds=delay) if start_time > now: # Don't allow creating events in the future. break runs_counts[runs_made] += 1 return events_count
def test_process_instance_event_duplicate_start( self, mock_calculate_concurrent_usage_task, mock_get_last_scheduled_concurrent_usage_calculation_task, ): """ Test that recalculate works when a duplicate start event is introduced. Initial Runs (5,7): [ ### ] New power on event at (1,) results in the run being updated: [####### ] New power on event at (3,) results in the run not being updated: [####### ] """ instance_type = "t1.potato" instance = api_helper.generate_instance(self.account) run_time = [( util_helper.utc_dt(2018, 1, 5, 0, 0, 0), util_helper.utc_dt(2018, 1, 7, 0, 0, 0), )] instance_events = api_helper.generate_instance_events( instance, run_time, instance_type=instance_type) api_helper.recalculate_runs_from_events(instance_events) first_start = util_helper.utc_dt(2018, 1, 1, 0, 0, 0) instance_event = api_helper.generate_single_instance_event( instance=instance, occurred_at=first_start, event_type=InstanceEvent.TYPE.power_on, instance_type=instance_type, ) tasks.process_instance_event(instance_event) runs = list(Run.objects.all()) self.assertEqual(1, len(runs)) self.assertEqual(run_time[0][1], runs[0].end_time) self.assertEqual(first_start, runs[0].start_time) second_start = util_helper.utc_dt(2018, 1, 3, 0, 0, 0) duplicate_start_event = api_helper.generate_single_instance_event( instance=instance, occurred_at=second_start, event_type=InstanceEvent.TYPE.power_on, instance_type=instance_type, ) tasks.process_instance_event(duplicate_start_event) runs = list(Run.objects.all()) self.assertEqual(1, len(runs)) self.assertEqual(run_time[0][1], runs[0].end_time) self.assertEqual(first_start, runs[0].start_time)