예제 #1
0
    def to_model(cls, dto_object: AppStatsDto) -> Optional[AppStats]:

        if dto_object.was_not_found():
            return None

        app_stats = AppStats()
        app_stats.cpu_usage = float(dto_object.stats.cpu_pct) / 100
        app_stats.mem_usage = float(dto_object.stats.ram_pct) / 100

        return app_stats
    async def test_get_app_stats_existing_app_id(self):
        scaler = AsgardInterface()

        with aioresponses() as rsps:
            payload = {
                "stats": {
                    "type": "ASGARD",
                    "errors": {},
                    "cpu_pct": "0.93",
                    "ram_pct": "8.91",
                    "cpu_thr_pct": "0.06",
                }
            }
            app = ScalableApp("app_test1")

            rsps.get(
                f"{settings.ASGARD_API_ADDRESS}/apps/{app.id}/stats/avg-1min",
                status=200,
                payload=payload,
            )

            fixture = AppStats(cpu_usage=0.93, mem_usage=8.91)

            app_stats = await scaler.get_app_stats(app)

            self.assertEqual(fixture.cpu_usage, app_stats.cpu_usage)
            self.assertEqual(fixture.mem_usage, app_stats.mem_usage)
예제 #3
0
    async def test_scales_cpu_and_mem_to_correct_value(self):
        apps = [
            ScalableApp(
                "test1",
                cpu_allocated=3.5,
                mem_allocated=1.0,
                cpu_threshold=0.1,
                mem_threshold=0.1,
                app_stats=AppStats(cpu_usage=1.0, mem_usage=1.0),
            ),
            ScalableApp(
                "test2",
                cpu_allocated=3.5,
                mem_allocated=1.0,
                cpu_threshold=0.5,
                mem_threshold=0.7,
                app_stats=AppStats(cpu_usage=1.0, mem_usage=1.0),
            ),
        ]

        decider = DecisionComponent()
        decisions = decider.decide_scaling_actions(apps)

        self.assertEqual(2, len(decisions), "makes decisions for both apps")
        self.assertEqual("test1", decisions[0].id,
                         "returns correct apps in correct order")
        self.assertEqual("test2", decisions[1].id,
                         "returns correct apps in correct order")
        self.assertEqual(35, decisions[0].cpu,
                         "decides correct values for cpu")
        self.assertEqual(10, decisions[0].mem,
                         "decides correct values for memory")
        self.assertEqual(7, decisions[1].cpu, "decides correct values for cpu")
        self.assertEqual(
            1.4286,
            round(decisions[1].mem, 4),
            "decides correct values for memory",
        )
예제 #4
0
    async def test_does_not_make_any_decision_when_everything_is_ignored(self):
        apps = [
            ScalableApp(
                "test",
                cpu_allocated=1.0,
                mem_allocated=1.0,
                cpu_threshold=None,
                mem_threshold=None,
                app_stats=AppStats(cpu_usage=0.8, mem_usage=0.3),
            )
        ]

        decider = DecisionComponent()
        decisions = decider.decide_scaling_actions(apps)

        self.assertEqual(0, len(decisions), "does not return any decisions")
예제 #5
0
    async def test_scales_memory_to_correct_value(self):
        apps = [
            ScalableApp(
                "test",
                cpu_allocated=0.5,
                mem_allocated=128,
                mem_threshold=0.2,
                app_stats=AppStats(cpu_usage=0.4129, mem_usage=0.6262),
            )
        ]

        decider = DecisionComponent()
        decisions = decider.decide_scaling_actions(apps)

        self.assertEqual(1, len(decisions), "did not return any decisions")
        self.assertEqual(400.768, decisions[0].mem)
예제 #6
0
    async def test_does_not_make_decision_when_app_is_using_max_mem_and_decision_would_upscale(
            self):
        apps = [
            ScalableApp(
                "test",
                cpu_allocated=0.5,
                mem_allocated=128,
                mem_threshold=0.75,
                app_stats=AppStats(cpu_usage=0.1, mem_usage=1.0),
                max_mem_scale_limit=128,
            )
        ]

        decider = DecisionComponent()
        decisions = decider.decide_scaling_actions(apps)

        self.assertEqual(0, len(decisions), "a decision was made")
예제 #7
0
    async def test_does_not_scale_app_when_difference_less_than_5_percent(
            self):
        apps = [
            ScalableApp(
                "test",
                cpu_allocated=1.0,
                mem_allocated=1.0,
                cpu_threshold=0.5,
                mem_threshold=0.7,
                app_stats=AppStats(cpu_usage=0.4501, mem_usage=0.7499),
            )
        ]

        decider = DecisionComponent()
        decisions = decider.decide_scaling_actions(apps)

        self.assertEqual(0, len(decisions))
예제 #8
0
    async def test_scales_app_when_difference_greater_than_5_percent(self):
        apps = [
            ScalableApp(
                "test",
                cpu_allocated=1.0,
                mem_allocated=1.0,
                cpu_threshold=0.5,
                mem_threshold=0.7,
                app_stats=AppStats(cpu_usage=0.4499, mem_usage=0.7501),
            )
        ]

        decider = DecisionComponent()
        decisions = decider.decide_scaling_actions(apps)

        self.assertEqual(1, len(decisions))
        self.assertEqual("test", decisions[0].id)
예제 #9
0
    def test_logs_memory_downscaling_decisions(self):
        apps = [
            ScalableApp(
                "test",
                cpu_allocated=0.5,
                mem_allocated=128,
                mem_threshold=1,
                app_stats=AppStats(cpu_usage=0.1, mem_usage=0.1),
            )
        ]
        mock_logger = NonCallableMock()

        decider = DecisionComponent(logger=mock_logger)
        decisions = decider.decide_scaling_actions(apps)

        mock_logger.info.assert_called()

        logged_dict = mock_logger.info.call_args[0][0]

        self.assertIn("appname", logged_dict, "did not log correct app id")
        self.assertEqual(apps[0].id, logged_dict["appname"],
                         "did not log correct app id")

        self.assertIn("event", logged_dict, "did not log an event")
        self.assertEqual(
            DecisionEvents.MEM_SCALE_DOWN,
            logged_dict["event"],
            "did not log correct event",
        )

        self.assertIn("previous_value", logged_dict,
                      "did not log previous memory value")
        self.assertEqual(
            apps[0].mem_allocated,
            logged_dict["previous_value"],
            "did not log correct previous memory value",
        )

        self.assertIn("new_value", logged_dict, "did not log new memory value")
        self.assertEqual(
            decisions[0].mem,
            logged_dict["new_value"],
            "did not log correct new memory value",
        )
    async def test_logs_cpu_upscaling_decisions(self):
        apps = [
            ScalableApp(
                "test",
                cpu_allocated=0.5,
                mem_allocated=128,
                cpu_threshold=0.2,
                app_stats=AppStats(cpu_usage=100, mem_usage=100),
            )
        ]
        mock_logger = NonCallableMock()

        decider = DecisionComponent(logger=mock_logger)
        decisions = decider.decide_scaling_actions(apps)

        mock_logger.info.assert_called()

        logged_dict = mock_logger.info.call_args[0][0]

        self.assertIn("appname", logged_dict, "did not log correct app id")
        self.assertEqual(apps[0].id, logged_dict["appname"],
                         "did not log correct app id")

        self.assertIn("event", logged_dict, "did not log an event")
        self.assertEqual(
            DecisionEvents.CPU_SCALE_UP,
            logged_dict["event"],
            "did not log correct event",
        )

        self.assertIn("previous_value", logged_dict,
                      "did not log previous CPU value")
        self.assertEqual(
            logged_dict["previous_value"],
            apps[0].cpu_allocated,
            "did not log correct previous CPU value",
        )

        self.assertIn("new_value", logged_dict, "did not log new CPU value")
        self.assertEqual(
            logged_dict["new_value"],
            decisions[0].cpu,
            "did not log correct new CPU value",
        )
예제 #11
0
    async def test_does_not_make_memory_decision_when_memory_is_ignored(self):
        apps = [
            ScalableApp(
                "test",
                cpu_allocated=1.0,
                mem_allocated=1.0,
                cpu_threshold=0.7,
                mem_threshold=None,
                app_stats=AppStats(cpu_usage=0.8, mem_usage=0.3),
            )
        ]

        decider = DecisionComponent()
        decisions = decider.decide_scaling_actions(apps)

        self.assertEqual(1, len(decisions), "makes decision for the app only")
        self.assertEqual("test", decisions[0].id, "returns the correct app")
        self.assertNotEqual(None, decisions[0].cpu, "returns cpu decision")
        self.assertEqual(None, decisions[0].mem,
                         "does not return memory decision")
예제 #12
0
    async def test_does_not_scale_cpu_above_max_scale_limit(self):
        max_cpu_limit = float("-inf")
        min_cpu_limit = float("-inf")
        apps = [
            ScalableApp(
                "test",
                cpu_allocated=0.5,
                mem_allocated=128,
                cpu_threshold=0.2,
                app_stats=AppStats(cpu_usage=0.4129, mem_usage=0.8),
                max_cpu_scale_limit=max_cpu_limit,
                min_cpu_scale_limit=min_cpu_limit,
            )
        ]

        decider = DecisionComponent()
        decisions = decider.decide_scaling_actions(apps)

        self.assertEqual(1, len(decisions), "did not return any decisions")
        self.assertLessEqual(
            max_cpu_limit,
            decisions[0].cpu,
            "cpu value is greater than the max limit",
        )
예제 #13
0
    async def test_does_not_scale_mem_below_min_scale_limit(self):
        min_mem_limit = float("inf")
        max_mem_limit = float("inf")
        apps = [
            ScalableApp(
                "test",
                cpu_allocated=0.5,
                mem_allocated=128,
                mem_threshold=0.5,
                app_stats=AppStats(cpu_usage=0.4129, mem_usage=0.35),
                min_mem_scale_limit=min_mem_limit,
                max_mem_scale_limit=max_mem_limit,
            )
        ]

        decider = DecisionComponent()
        decisions = decider.decide_scaling_actions(apps)

        self.assertEqual(1, len(decisions), "did not return any decisions")
        self.assertGreaterEqual(
            min_mem_limit,
            decisions[0].mem,
            "mem value is less than the min limit",
        )
예제 #14
0
    def test_logs_cpu_and_memory_scaling_decisions(self):
        apps = [
            ScalableApp(
                "test",
                cpu_allocated=0.5,
                mem_allocated=128,
                mem_threshold=1,
                cpu_threshold=0.2,
                app_stats=AppStats(cpu_usage=1.0, mem_usage=0.1),
            )
        ]
        mock_logger = NonCallableMock()

        decider = DecisionComponent(logger=mock_logger)
        decisions = decider.decide_scaling_actions(apps)

        mock_logger.info.assert_called()

        logger_calls = [call[0][0] for call in mock_logger.info.call_args_list]

        self.assertEqual(len(logger_calls), 2, "did not call log.info 2 times")

        logger_calls.sort(key=lambda call: call["event"])

        cpu_log_dict = logger_calls[0]
        mem_log_dict = logger_calls[1]

        self.assertIn("appname", cpu_log_dict, "did not log correct app id")
        self.assertEqual(mem_log_dict["appname"], apps[0].id,
                         "did not log correct app id")

        self.assertIn("event", cpu_log_dict, "did not log an event")
        self.assertEqual(
            DecisionEvents.CPU_SCALE_UP,
            cpu_log_dict["event"],
            "did not log correct event",
        )

        self.assertIn("previous_value", cpu_log_dict,
                      "did not log previous memory value")
        self.assertEqual(
            0.5,
            cpu_log_dict["previous_value"],
            "did not log correct previous memory value",
        )

        self.assertIn("new_value", cpu_log_dict,
                      "did not log new memory value")
        self.assertEqual(
            decisions[0].cpu,
            cpu_log_dict["new_value"],
            "did not log correct new memory value",
        )

        self.assertIn("appname", mem_log_dict, "did not log correct app id")
        self.assertEqual(apps[0].id, mem_log_dict["appname"],
                         "did not log correct app id")

        self.assertIn("event", mem_log_dict, "did not log an event")
        self.assertEqual(
            DecisionEvents.MEM_SCALE_DOWN,
            mem_log_dict["event"],
            "did not log correct event",
        )

        self.assertIn("previous_value", mem_log_dict,
                      "did not log previous memory value")
        self.assertEqual(
            apps[0].mem_allocated,
            mem_log_dict["previous_value"],
            "did not log correct previous memory value",
        )

        self.assertIn("new_value", mem_log_dict,
                      "did not log new memory value")
        self.assertEqual(
            decisions[0].mem,
            mem_log_dict["new_value"],
            "did not log correct new memory value",
        )