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()
Exemple #2
0
    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)
Exemple #4
0
    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())
Exemple #5
0
    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()
Exemple #6
0
    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()
Exemple #7
0
    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
Exemple #8
0
    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)