From 18a1fd244676aa6e352297e4d8dbf643ad1255eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B0=D0=B2=D0=B5=D0=BB=20=D0=96=D1=83=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2?= <33721692+LeaveMyYard@users.noreply.github.com> Date: Tue, 16 May 2023 11:47:03 +0300 Subject: Rework severity calculation to use absolute values --- robusta_krr/core/models/result.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/robusta_krr/core/models/result.py b/robusta_krr/core/models/result.py index 7c686f7..c441319 100644 --- a/robusta_krr/core/models/result.py +++ b/robusta_krr/core/models/result.py @@ -31,23 +31,36 @@ class Severity(str, enum.Enum): }[self] @classmethod - def calculate(cls, current: RecommendationValue, recommended: RecommendationValue) -> Severity: + def calculate(cls, current: RecommendationValue, recommended: RecommendationValue, resource_type: ResourceType) -> Severity: if isinstance(recommended, str) or isinstance(current, str): return cls.UNKNOWN if current is None and recommended is None: - return cls.OK + return cls.GOOD if current is None or recommended is None: return cls.WARNING - diff = (current - recommended) / recommended - - if diff > 1.0 or diff < -0.5: - return cls.CRITICAL - elif diff > 0.5 or diff < -0.25: - return cls.WARNING + diff = abs(current - recommended) + + if resource_type == ResourceType.CPU: + if diff >= 0.5: + return cls.CRITICAL + elif diff >= 0.25: + return cls.WARNING + elif diff >= 0.1: + return cls.OK + else: + return cls.GOOD else: - return cls.GOOD + diff_megabytes = diff / 1024 / 1024 + if diff_megabytes >= 500: + return cls.CRITICAL + elif diff_megabytes >= 250: + return cls.WARNING + elif diff_megabytes >= 100: + return cls.OK + else: + return cls.GOOD class Recommendation(pd.BaseModel): @@ -74,7 +87,7 @@ class ResourceScan(pd.BaseModel): current = getattr(object.allocations, selector).get(resource_type) recommended = getattr(recommendation, selector).get(resource_type) - current_severity = Severity.calculate(current, recommended) + current_severity = Severity.calculate(current, recommended, resource_type) getattr(recommendation_processed, selector)[resource_type] = Recommendation( value=recommended, severity=current_severity -- cgit v1.2.3 From 8da544e4ca733a9dcf613c7341b75fa256dd824f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B0=D0=B2=D0=B5=D0=BB=20=D0=96=D1=83=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2?= <33721692+LeaveMyYard@users.noreply.github.com> Date: Thu, 25 May 2023 21:25:47 +0300 Subject: Fix score calculation --- robusta_krr/core/models/result.py | 50 ++++++++++++++++----------------------- robusta_krr/formatters/table.py | 3 +-- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/robusta_krr/core/models/result.py b/robusta_krr/core/models/result.py index 8e02d0c..d4e042f 100644 --- a/robusta_krr/core/models/result.py +++ b/robusta_krr/core/models/result.py @@ -1,7 +1,6 @@ from __future__ import annotations import enum -import itertools from datetime import datetime from typing import Any, Optional, Union @@ -32,7 +31,9 @@ class Severity(str, enum.Enum): }[self] @classmethod - def calculate(cls, current: RecommendationValue, recommended: RecommendationValue, resource_type: ResourceType) -> Severity: + def calculate( + cls, current: RecommendationValue, recommended: RecommendationValue, resource_type: ResourceType + ) -> Severity: if isinstance(recommended, str) or isinstance(current, str): return cls.UNKNOWN @@ -143,18 +144,8 @@ class Result(pd.BaseModel): return _formatter.format(self) @staticmethod - def __percentage_difference(current: RecommendationValue, recommended: RecommendationValue) -> float: - """Get the percentage difference between two numbers. - - Args: - current: The current value. - recommended: The recommended value. - - Returns: - The percentage difference. - """ - - return 1 + def __scan_cost(scan: ResourceScan) -> float: + return 0.7 if scan.severity == Severity.WARNING else 1 if scan.severity == Severity.CRITICAL else 0 def __calculate_score(self) -> int: """Get the score of the result. @@ -163,18 +154,19 @@ class Result(pd.BaseModel): The score of the result. """ - total_diff = 0.0 - for scan, resource_type in itertools.product(self.scans, ResourceType): - total_diff += self.__percentage_difference( - scan.object.allocations.requests[resource_type], scan.recommended.requests[resource_type] - ) - total_diff += self.__percentage_difference( - scan.object.allocations.limits[resource_type], scan.recommended.limits[resource_type] - ) - - if len(self.scans) == 0: - return 0 - - return int( - max(0, round(100 - total_diff / len(self.scans) / len(ResourceType) / 50, 2)) - ) # 50 is just a constant + score = sum(self.__scan_cost(scan) for scan in self.scans) + return int((len(self.scans) - score) / len(self.scans) * 100) + + @property + def score_letter(self) -> str: + return ( + "F" + if self.score < 30 + else "D" + if self.score < 55 + else "C" + if self.score < 70 + else "B" + if self.score < 90 + else "A" + ) diff --git a/robusta_krr/formatters/table.py b/robusta_krr/formatters/table.py index 8ef1696..6faef56 100644 --- a/robusta_krr/formatters/table.py +++ b/robusta_krr/formatters/table.py @@ -55,8 +55,7 @@ class TableFormatter(BaseFormatter): title=f"\n{result.description}\n" if result.description else None, title_justify="left", title_style="", - # TODO: Fix points calculation at [MAIN-270] - # caption=f"Scan result ({result.score} points)", + caption=f"{result.score} points - {result.score_letter}", ) table.add_column("Number", justify="right", no_wrap=True) -- cgit v1.2.3 From 6f118c1973cf64c747c5842e49fcdeb6b59f7e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B0=D0=B2=D0=B5=D0=BB=20=D0=96=D1=83=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2?= <33721692+LeaveMyYard@users.noreply.github.com> Date: Thu, 25 May 2023 21:54:32 +0300 Subject: Refactor Severity calcs using a strategy pattern --- robusta_krr/core/models/result.py | 56 +--------------------- robusta_krr/core/models/severity.py | 36 ++++++++++++++ .../core/models/severity_calculator/__init__.py | 4 ++ .../models/severity_calculator/base_calculator.py | 35 ++++++++++++++ .../models/severity_calculator/cpu_calculator.py | 27 +++++++++++ .../severity_calculator/default_calculator.py | 11 +++++ .../severity_calculator/memory_calculator.py | 27 +++++++++++ 7 files changed, 141 insertions(+), 55 deletions(-) create mode 100644 robusta_krr/core/models/severity.py create mode 100644 robusta_krr/core/models/severity_calculator/__init__.py create mode 100644 robusta_krr/core/models/severity_calculator/base_calculator.py create mode 100644 robusta_krr/core/models/severity_calculator/cpu_calculator.py create mode 100644 robusta_krr/core/models/severity_calculator/default_calculator.py create mode 100644 robusta_krr/core/models/severity_calculator/memory_calculator.py diff --git a/robusta_krr/core/models/result.py b/robusta_krr/core/models/result.py index d4e042f..46512bb 100644 --- a/robusta_krr/core/models/result.py +++ b/robusta_krr/core/models/result.py @@ -1,6 +1,5 @@ from __future__ import annotations -import enum from datetime import datetime from typing import Any, Optional, Union @@ -9,60 +8,7 @@ import pydantic as pd from robusta_krr.core.abstract.formatters import BaseFormatter from robusta_krr.core.models.allocations import RecommendationValue, ResourceAllocations, ResourceType from robusta_krr.core.models.objects import K8sObjectData - - -class Severity(str, enum.Enum): - """The severity of the scan.""" - - UNKNOWN = "UNKNOWN" - GOOD = "GOOD" - OK = "OK" - WARNING = "WARNING" - CRITICAL = "CRITICAL" - - @property - def color(self) -> str: - return { - self.UNKNOWN: "dim", - self.GOOD: "green", - self.OK: "gray", - self.WARNING: "yellow", - self.CRITICAL: "red", - }[self] - - @classmethod - def calculate( - cls, current: RecommendationValue, recommended: RecommendationValue, resource_type: ResourceType - ) -> Severity: - if isinstance(recommended, str) or isinstance(current, str): - return cls.UNKNOWN - - if current is None and recommended is None: - return cls.GOOD - if current is None or recommended is None: - return cls.WARNING - - diff = abs(current - recommended) - - if resource_type == ResourceType.CPU: - if diff >= 0.5: - return cls.CRITICAL - elif diff >= 0.25: - return cls.WARNING - elif diff >= 0.1: - return cls.OK - else: - return cls.GOOD - else: - diff_megabytes = diff / 1024 / 1024 - if diff_megabytes >= 500: - return cls.CRITICAL - elif diff_megabytes >= 250: - return cls.WARNING - elif diff_megabytes >= 100: - return cls.OK - else: - return cls.GOOD +from robusta_krr.core.models.severity import Severity class Recommendation(pd.BaseModel): diff --git a/robusta_krr/core/models/severity.py b/robusta_krr/core/models/severity.py new file mode 100644 index 0000000..cbe45a2 --- /dev/null +++ b/robusta_krr/core/models/severity.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +import enum + +from robusta_krr.core.models.allocations import RecommendationValue, ResourceType + + +class Severity(str, enum.Enum): + """The severity of the scan.""" + + UNKNOWN = "UNKNOWN" + GOOD = "GOOD" + OK = "OK" + WARNING = "WARNING" + CRITICAL = "CRITICAL" + + @property + def color(self) -> str: + return { + self.UNKNOWN: "dim", + self.GOOD: "green", + self.OK: "gray", + self.WARNING: "yellow", + self.CRITICAL: "red", + }[self] + + @classmethod + def calculate( + cls, current: RecommendationValue, recommended: RecommendationValue, resource_type: ResourceType + ) -> Severity: + from robusta_krr.core.models.severity_calculator import BaseSeverityCalculator + + if isinstance(recommended, str) or isinstance(current, str): + return cls.UNKNOWN + + return BaseSeverityCalculator.get_by_resource(resource_type).calculate(current, recommended) diff --git a/robusta_krr/core/models/severity_calculator/__init__.py b/robusta_krr/core/models/severity_calculator/__init__.py new file mode 100644 index 0000000..edaafd7 --- /dev/null +++ b/robusta_krr/core/models/severity_calculator/__init__.py @@ -0,0 +1,4 @@ +from .base_calculator import BaseSeverityCalculator, bind_calculator +from .cpu_calculator import CPUSeverityCalculator +from .default_calculator import DefaultSeverityCalculator +from .memory_calculator import MemorySeverityCalculator diff --git a/robusta_krr/core/models/severity_calculator/base_calculator.py b/robusta_krr/core/models/severity_calculator/base_calculator.py new file mode 100644 index 0000000..832b5a5 --- /dev/null +++ b/robusta_krr/core/models/severity_calculator/base_calculator.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +import abc +from typing import Callable, Optional, TypeVar + +from robusta_krr.core.models.allocations import ResourceType +from robusta_krr.core.models.severity import Severity + + +class BaseSeverityCalculator(abc.ABC): + # Is here as we are creating this object in get_by_resource method, so it can not have any arguments + def __init__(self) -> None: + ... + + @abc.abstractmethod + def calculate(self, current: Optional[float], recommended: Optional[float]) -> Severity: + ... + + @staticmethod + def get_by_resource(resource: ResourceType) -> BaseSeverityCalculator: + from robusta_krr.core.models.severity_calculator.default_calculator import DefaultSeverityCalculator + + return SEVERITY_CALCULATORS_MAP.get(resource, DefaultSeverityCalculator)() + + +Self = TypeVar("Self", bound=BaseSeverityCalculator) +SEVERITY_CALCULATORS_MAP: dict[ResourceType, type[BaseSeverityCalculator]] = {} + + +def bind_calculator(resource_type: ResourceType) -> Callable[[type[Self]], type[Self]]: + def decorator(cls: type[Self]) -> type[Self]: + SEVERITY_CALCULATORS_MAP[resource_type] = cls + return cls + + return decorator diff --git a/robusta_krr/core/models/severity_calculator/cpu_calculator.py b/robusta_krr/core/models/severity_calculator/cpu_calculator.py new file mode 100644 index 0000000..965adec --- /dev/null +++ b/robusta_krr/core/models/severity_calculator/cpu_calculator.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from typing import Optional + +from robusta_krr.core.models.allocations import ResourceType +from robusta_krr.core.models.severity import Severity +from robusta_krr.core.models.severity_calculator.base_calculator import BaseSeverityCalculator, bind_calculator + + +@bind_calculator(ResourceType.CPU) +class CPUSeverityCalculator(BaseSeverityCalculator): + def calculate(self, current: Optional[float], recommended: Optional[float]) -> Severity: + if current is None and recommended is None: + return Severity.GOOD + if current is None or recommended is None: + return Severity.WARNING + + diff = abs(current - recommended) + + if diff >= 0.5: + return Severity.CRITICAL + elif diff >= 0.25: + return Severity.WARNING + elif diff >= 0.1: + return Severity.OK + else: + return Severity.GOOD diff --git a/robusta_krr/core/models/severity_calculator/default_calculator.py b/robusta_krr/core/models/severity_calculator/default_calculator.py new file mode 100644 index 0000000..adcb4af --- /dev/null +++ b/robusta_krr/core/models/severity_calculator/default_calculator.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from typing import Optional + +from robusta_krr.core.models.severity import Severity +from robusta_krr.core.models.severity_calculator.base_calculator import BaseSeverityCalculator + + +class DefaultSeverityCalculator(BaseSeverityCalculator): + def calculate(self, current: Optional[float], recommended: Optional[float]) -> Severity: + return Severity.UNKNOWN diff --git a/robusta_krr/core/models/severity_calculator/memory_calculator.py b/robusta_krr/core/models/severity_calculator/memory_calculator.py new file mode 100644 index 0000000..5948949 --- /dev/null +++ b/robusta_krr/core/models/severity_calculator/memory_calculator.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from typing import Optional + +from robusta_krr.core.models.allocations import ResourceType +from robusta_krr.core.models.severity import Severity +from robusta_krr.core.models.severity_calculator.base_calculator import BaseSeverityCalculator, bind_calculator + + +@bind_calculator(ResourceType.Memory) +class MemorySeverityCalculator(BaseSeverityCalculator): + def calculate(self, current: Optional[float], recommended: Optional[float]) -> Severity: + if current is None and recommended is None: + return Severity.GOOD + if current is None or recommended is None: + return Severity.WARNING + + diff = abs(current - recommended) / 1024 / 1024 + + if diff >= 500: + return Severity.CRITICAL + elif diff >= 250: + return Severity.WARNING + elif diff >= 100: + return Severity.OK + else: + return Severity.GOOD -- cgit v1.2.3 From 037cc5e0c4b57fc8a9f27e7933253f964d912e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B0=D0=B2=D0=B5=D0=BB=20=D0=96=D1=83=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2?= <33721692+LeaveMyYard@users.noreply.github.com> Date: Mon, 29 May 2023 10:01:18 +0300 Subject: Refactor severity calculators using functional approach --- examples/custom_severity_calculator.py | 46 ++++++++++ robusta_krr/api/models.py | 4 +- robusta_krr/core/models/severity.py | 100 ++++++++++++++++++++- .../core/models/severity_calculator/__init__.py | 4 - .../models/severity_calculator/base_calculator.py | 35 -------- .../models/severity_calculator/cpu_calculator.py | 27 ------ .../severity_calculator/default_calculator.py | 11 --- .../severity_calculator/memory_calculator.py | 27 ------ 8 files changed, 145 insertions(+), 109 deletions(-) create mode 100644 examples/custom_severity_calculator.py delete mode 100644 robusta_krr/core/models/severity_calculator/__init__.py delete mode 100644 robusta_krr/core/models/severity_calculator/base_calculator.py delete mode 100644 robusta_krr/core/models/severity_calculator/cpu_calculator.py delete mode 100644 robusta_krr/core/models/severity_calculator/default_calculator.py delete mode 100644 robusta_krr/core/models/severity_calculator/memory_calculator.py diff --git a/examples/custom_severity_calculator.py b/examples/custom_severity_calculator.py new file mode 100644 index 0000000..9754dd8 --- /dev/null +++ b/examples/custom_severity_calculator.py @@ -0,0 +1,46 @@ +# This is an example on how to create your own custom formatter + +from __future__ import annotations + +from typing import Optional + +import robusta_krr +from robusta_krr.api.models import Severity, ResourceType + + +@Severity.bind_calculator(ResourceType.CPU) +def percentage_severity_calculator( + current: Optional[float], recommended: Optional[float], resource_type: ResourceType +) -> Severity: + """ + This is an example on how to create your own custom severity calculator + You can use this decorator to bind a severity calculator function to a resource type. + The function will be called with the current value, the recommended value and the resource type. + The function should return a Severity enum value. + + If you have the same calculation for multiple resource types, you can use the `bind_calculator` decorator multiple times. + Then, the function will be called for each resource type and you can use the resource type to distinguish between them. + + Keep in mind that you can not choose the strategy for the resource type using CLI - the last one created for the resource type will be used. + """ + + if current is None and recommended is None: + return Severity.GOOD + if current is None or recommended is None: + return Severity.WARNING + + diff = abs(current - recommended) / current + if diff >= 0.5: + return Severity.CRITICAL + elif diff >= 0.25: + return Severity.WARNING + elif diff >= 0.1: + return Severity.OK + else: + return Severity.GOOD + + +# Running this file will register the formatter and make it available to the CLI +# Run it as `python ./custom_formatter.py simple --formater my_formatter` +if __name__ == "__main__": + robusta_krr.run() diff --git a/robusta_krr/api/models.py b/robusta_krr/api/models.py index be2a3ad..c6b3c99 100644 --- a/robusta_krr/api/models.py +++ b/robusta_krr/api/models.py @@ -1,7 +1,8 @@ from robusta_krr.core.abstract.strategies import HistoryData, ResourceRecommendation, RunResult from robusta_krr.core.models.allocations import RecommendationValue, ResourceAllocations, ResourceType from robusta_krr.core.models.objects import K8sObjectData -from robusta_krr.core.models.result import ResourceScan, Result, Severity +from robusta_krr.core.models.result import ResourceScan, Result +from robusta_krr.core.models.severity import Severity __all__ = [ "ResourceType", @@ -10,6 +11,7 @@ __all__ = [ "K8sObjectData", "Result", "Severity", + "bind_calculator", "ResourceScan", "ResourceRecommendation", "HistoryData", diff --git a/robusta_krr/core/models/severity.py b/robusta_krr/core/models/severity.py index cbe45a2..d2c942e 100644 --- a/robusta_krr/core/models/severity.py +++ b/robusta_krr/core/models/severity.py @@ -2,11 +2,17 @@ from __future__ import annotations import enum +from typing import Callable, Optional from robusta_krr.core.models.allocations import RecommendationValue, ResourceType class Severity(str, enum.Enum): - """The severity of the scan.""" + """ + The severity of the scan. + + The severity is calculated based on the difference between the current value and the recommended value. + You can override the severity calculation function by using the `bind_calculator` decorator from the same module. + """ UNKNOWN = "UNKNOWN" GOOD = "GOOD" @@ -28,9 +34,95 @@ class Severity(str, enum.Enum): def calculate( cls, current: RecommendationValue, recommended: RecommendationValue, resource_type: ResourceType ) -> Severity: - from robusta_krr.core.models.severity_calculator import BaseSeverityCalculator - if isinstance(recommended, str) or isinstance(current, str): return cls.UNKNOWN - return BaseSeverityCalculator.get_by_resource(resource_type).calculate(current, recommended) + return calculate_severity(current, recommended, resource_type) + + @staticmethod + def bind_calculator(resource_type: ResourceType) -> Callable[[SeverityCalculator], SeverityCalculator]: + """ + Bind a severity calculator function to a resource type. + Use this decorator to bind a severity calculator function to a resource type. + + Example: + >>> @bind_calculator(ResourceType.CPU) + >>> def cpu_severity_calculator(current: Optional[float], recommended: Optional[float], resource_type: ResourceType) -> Severity: + >>> if current is None and recommended is None: + >>> return Severity.GOOD + >>> if current is None or recommended is None: + >>> return Severity.WARNING + >>> + >>> return Severity.CRITICAL if abs(current - recommended) >= 0.5 else Severity.GOOD + """ + + def decorator(func: SeverityCalculator) -> SeverityCalculator: + SEVERITY_CALCULATORS_REGISTRY[resource_type] = func + return func + + return decorator + + +SeverityCalculator = Callable[[Optional[float], Optional[float], ResourceType], Severity] +SEVERITY_CALCULATORS_REGISTRY: dict[ResourceType, SeverityCalculator] = {} + + +def calculate_severity(current: Optional[float], recommended: Optional[float], resource_type: ResourceType) -> Severity: + """ + Calculate the severity of the scan based on the current value and the recommended value. + + This function will use the severity calculator function that is bound to the resource type. + If there is no calculator function bound to the resource type, it will use the default severity calculator function. + """ + + return SEVERITY_CALCULATORS_REGISTRY.get(resource_type, default_severity_calculator)( + current, recommended, resource_type + ) + + +def default_severity_calculator( + current: Optional[float], recommended: Optional[float], resource_type: ResourceType +) -> Severity: + return Severity.UNKNOWN + + +@Severity.bind_calculator(ResourceType.CPU) +def cpu_severity_calculator( + current: Optional[float], recommended: Optional[float], resource_type: ResourceType +) -> Severity: + if current is None and recommended is None: + return Severity.GOOD + if current is None or recommended is None: + return Severity.WARNING + + diff = abs(current - recommended) + + if diff >= 0.5: + return Severity.CRITICAL + elif diff >= 0.25: + return Severity.WARNING + elif diff >= 0.1: + return Severity.OK + else: + return Severity.GOOD + + +@Severity.bind_calculator(ResourceType.Memory) +def memory_severity_calculator( + current: Optional[float], recommended: Optional[float], resource_type: ResourceType +) -> Severity: + if current is None and recommended is None: + return Severity.GOOD + if current is None or recommended is None: + return Severity.WARNING + + diff = abs(current - recommended) / 1024 / 1024 + + if diff >= 500: + return Severity.CRITICAL + elif diff >= 250: + return Severity.WARNING + elif diff >= 100: + return Severity.OK + else: + return Severity.GOOD diff --git a/robusta_krr/core/models/severity_calculator/__init__.py b/robusta_krr/core/models/severity_calculator/__init__.py deleted file mode 100644 index edaafd7..0000000 --- a/robusta_krr/core/models/severity_calculator/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .base_calculator import BaseSeverityCalculator, bind_calculator -from .cpu_calculator import CPUSeverityCalculator -from .default_calculator import DefaultSeverityCalculator -from .memory_calculator import MemorySeverityCalculator diff --git a/robusta_krr/core/models/severity_calculator/base_calculator.py b/robusta_krr/core/models/severity_calculator/base_calculator.py deleted file mode 100644 index 832b5a5..0000000 --- a/robusta_krr/core/models/severity_calculator/base_calculator.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -import abc -from typing import Callable, Optional, TypeVar - -from robusta_krr.core.models.allocations import ResourceType -from robusta_krr.core.models.severity import Severity - - -class BaseSeverityCalculator(abc.ABC): - # Is here as we are creating this object in get_by_resource method, so it can not have any arguments - def __init__(self) -> None: - ... - - @abc.abstractmethod - def calculate(self, current: Optional[float], recommended: Optional[float]) -> Severity: - ... - - @staticmethod - def get_by_resource(resource: ResourceType) -> BaseSeverityCalculator: - from robusta_krr.core.models.severity_calculator.default_calculator import DefaultSeverityCalculator - - return SEVERITY_CALCULATORS_MAP.get(resource, DefaultSeverityCalculator)() - - -Self = TypeVar("Self", bound=BaseSeverityCalculator) -SEVERITY_CALCULATORS_MAP: dict[ResourceType, type[BaseSeverityCalculator]] = {} - - -def bind_calculator(resource_type: ResourceType) -> Callable[[type[Self]], type[Self]]: - def decorator(cls: type[Self]) -> type[Self]: - SEVERITY_CALCULATORS_MAP[resource_type] = cls - return cls - - return decorator diff --git a/robusta_krr/core/models/severity_calculator/cpu_calculator.py b/robusta_krr/core/models/severity_calculator/cpu_calculator.py deleted file mode 100644 index 965adec..0000000 --- a/robusta_krr/core/models/severity_calculator/cpu_calculator.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from robusta_krr.core.models.allocations import ResourceType -from robusta_krr.core.models.severity import Severity -from robusta_krr.core.models.severity_calculator.base_calculator import BaseSeverityCalculator, bind_calculator - - -@bind_calculator(ResourceType.CPU) -class CPUSeverityCalculator(BaseSeverityCalculator): - def calculate(self, current: Optional[float], recommended: Optional[float]) -> Severity: - if current is None and recommended is None: - return Severity.GOOD - if current is None or recommended is None: - return Severity.WARNING - - diff = abs(current - recommended) - - if diff >= 0.5: - return Severity.CRITICAL - elif diff >= 0.25: - return Severity.WARNING - elif diff >= 0.1: - return Severity.OK - else: - return Severity.GOOD diff --git a/robusta_krr/core/models/severity_calculator/default_calculator.py b/robusta_krr/core/models/severity_calculator/default_calculator.py deleted file mode 100644 index adcb4af..0000000 --- a/robusta_krr/core/models/severity_calculator/default_calculator.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from robusta_krr.core.models.severity import Severity -from robusta_krr.core.models.severity_calculator.base_calculator import BaseSeverityCalculator - - -class DefaultSeverityCalculator(BaseSeverityCalculator): - def calculate(self, current: Optional[float], recommended: Optional[float]) -> Severity: - return Severity.UNKNOWN diff --git a/robusta_krr/core/models/severity_calculator/memory_calculator.py b/robusta_krr/core/models/severity_calculator/memory_calculator.py deleted file mode 100644 index 5948949..0000000 --- a/robusta_krr/core/models/severity_calculator/memory_calculator.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from robusta_krr.core.models.allocations import ResourceType -from robusta_krr.core.models.severity import Severity -from robusta_krr.core.models.severity_calculator.base_calculator import BaseSeverityCalculator, bind_calculator - - -@bind_calculator(ResourceType.Memory) -class MemorySeverityCalculator(BaseSeverityCalculator): - def calculate(self, current: Optional[float], recommended: Optional[float]) -> Severity: - if current is None and recommended is None: - return Severity.GOOD - if current is None or recommended is None: - return Severity.WARNING - - diff = abs(current - recommended) / 1024 / 1024 - - if diff >= 500: - return Severity.CRITICAL - elif diff >= 250: - return Severity.WARNING - elif diff >= 100: - return Severity.OK - else: - return Severity.GOOD -- cgit v1.2.3 From cf815779506f77f3720dec6ad9821295f7afc389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B0=D0=B2=D0=B5=D0=BB=20=D0=96=D1=83=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2?= <33721692+LeaveMyYard@users.noreply.github.com> Date: Mon, 29 May 2023 15:15:28 +0300 Subject: Rename decorator to register_severity_calculator --- examples/custom_severity_calculator.py | 4 +-- robusta_krr/api/models.py | 3 ++- robusta_krr/core/models/objects.py | 5 ++++ robusta_krr/core/models/severity.py | 48 +++++++++++++++++----------------- robusta_krr/formatters/json.py | 2 +- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/examples/custom_severity_calculator.py b/examples/custom_severity_calculator.py index 9754dd8..5978bfc 100644 --- a/examples/custom_severity_calculator.py +++ b/examples/custom_severity_calculator.py @@ -5,10 +5,10 @@ from __future__ import annotations from typing import Optional import robusta_krr -from robusta_krr.api.models import Severity, ResourceType +from robusta_krr.api.models import Severity, ResourceType, register_severity_calculator -@Severity.bind_calculator(ResourceType.CPU) +@register_severity_calculator(ResourceType.CPU) def percentage_severity_calculator( current: Optional[float], recommended: Optional[float], resource_type: ResourceType ) -> Severity: diff --git a/robusta_krr/api/models.py b/robusta_krr/api/models.py index c6b3c99..5340f29 100644 --- a/robusta_krr/api/models.py +++ b/robusta_krr/api/models.py @@ -2,7 +2,7 @@ from robusta_krr.core.abstract.strategies import HistoryData, ResourceRecommenda from robusta_krr.core.models.allocations import RecommendationValue, ResourceAllocations, ResourceType from robusta_krr.core.models.objects import K8sObjectData from robusta_krr.core.models.result import ResourceScan, Result -from robusta_krr.core.models.severity import Severity +from robusta_krr.core.models.severity import Severity, register_severity_calculator __all__ = [ "ResourceType", @@ -11,6 +11,7 @@ __all__ = [ "K8sObjectData", "Result", "Severity", + "register_severity_calculator", "bind_calculator", "ResourceScan", "ResourceRecommendation", diff --git a/robusta_krr/core/models/objects.py b/robusta_krr/core/models/objects.py index 21f5d61..8948319 100644 --- a/robusta_krr/core/models/objects.py +++ b/robusta_krr/core/models/objects.py @@ -39,3 +39,8 @@ class K8sObjectData(pd.BaseModel): @property def pods_count(self) -> int: return len(self.pods) + + class Config: + json_encoders = { + PodData: lambda pod: pod.name, + } diff --git a/robusta_krr/core/models/severity.py b/robusta_krr/core/models/severity.py index d2c942e..ec1fad0 100644 --- a/robusta_krr/core/models/severity.py +++ b/robusta_krr/core/models/severity.py @@ -39,28 +39,28 @@ class Severity(str, enum.Enum): return calculate_severity(current, recommended, resource_type) - @staticmethod - def bind_calculator(resource_type: ResourceType) -> Callable[[SeverityCalculator], SeverityCalculator]: - """ - Bind a severity calculator function to a resource type. - Use this decorator to bind a severity calculator function to a resource type. - - Example: - >>> @bind_calculator(ResourceType.CPU) - >>> def cpu_severity_calculator(current: Optional[float], recommended: Optional[float], resource_type: ResourceType) -> Severity: - >>> if current is None and recommended is None: - >>> return Severity.GOOD - >>> if current is None or recommended is None: - >>> return Severity.WARNING - >>> - >>> return Severity.CRITICAL if abs(current - recommended) >= 0.5 else Severity.GOOD - """ - - def decorator(func: SeverityCalculator) -> SeverityCalculator: - SEVERITY_CALCULATORS_REGISTRY[resource_type] = func - return func - - return decorator + +def register_severity_calculator(resource_type: ResourceType) -> Callable[[SeverityCalculator], SeverityCalculator]: + """ + Bind a severity calculator function to a resource type. + Use this decorator to bind a severity calculator function to a resource type. + + Example: + >>> @bind_severity_calculator(ResourceType.CPU) + >>> def cpu_severity_calculator(current: Optional[float], recommended: Optional[float], resource_type: ResourceType) -> Severity: + >>> if current is None and recommended is None: + >>> return Severity.GOOD + >>> if current is None or recommended is None: + >>> return Severity.WARNING + >>> + >>> return Severity.CRITICAL if abs(current - recommended) >= 0.5 else Severity.GOOD + """ + + def decorator(func: SeverityCalculator) -> SeverityCalculator: + SEVERITY_CALCULATORS_REGISTRY[resource_type] = func + return func + + return decorator SeverityCalculator = Callable[[Optional[float], Optional[float], ResourceType], Severity] @@ -86,7 +86,7 @@ def default_severity_calculator( return Severity.UNKNOWN -@Severity.bind_calculator(ResourceType.CPU) +@register_severity_calculator(ResourceType.CPU) def cpu_severity_calculator( current: Optional[float], recommended: Optional[float], resource_type: ResourceType ) -> Severity: @@ -107,7 +107,7 @@ def cpu_severity_calculator( return Severity.GOOD -@Severity.bind_calculator(ResourceType.Memory) +@register_severity_calculator(ResourceType.Memory) def memory_severity_calculator( current: Optional[float], recommended: Optional[float], resource_type: ResourceType ) -> Severity: diff --git a/robusta_krr/formatters/json.py b/robusta_krr/formatters/json.py index 2c3a51e..dfded37 100644 --- a/robusta_krr/formatters/json.py +++ b/robusta_krr/formatters/json.py @@ -18,4 +18,4 @@ class JSONFormatter(BaseFormatter): :rtype: str """ - return result.json(indent=2) + return result.json(indent=2, models_as_dict=False) -- cgit v1.2.3 From f216136c111fed140b897da35037ce1541219fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B0=D0=B2=D0=B5=D0=BB=20=D0=96=D1=83=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2?= <33721692+LeaveMyYard@users.noreply.github.com> Date: Mon, 29 May 2023 15:18:34 +0300 Subject: Revert accidentally commited changes --- robusta_krr/core/models/objects.py | 5 ----- robusta_krr/formatters/json.py | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/robusta_krr/core/models/objects.py b/robusta_krr/core/models/objects.py index 8948319..21f5d61 100644 --- a/robusta_krr/core/models/objects.py +++ b/robusta_krr/core/models/objects.py @@ -39,8 +39,3 @@ class K8sObjectData(pd.BaseModel): @property def pods_count(self) -> int: return len(self.pods) - - class Config: - json_encoders = { - PodData: lambda pod: pod.name, - } diff --git a/robusta_krr/formatters/json.py b/robusta_krr/formatters/json.py index dfded37..2c3a51e 100644 --- a/robusta_krr/formatters/json.py +++ b/robusta_krr/formatters/json.py @@ -18,4 +18,4 @@ class JSONFormatter(BaseFormatter): :rtype: str """ - return result.json(indent=2, models_as_dict=False) + return result.json(indent=2) -- cgit v1.2.3