diff options
| author | Wesley Haakman <25249425+whaakman@users.noreply.github.com> | 2024-03-18 17:00:26 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-03-18 18:00:26 +0200 |
| commit | 0251ffc903efff22b965503ed2db92d5404d5529 (patch) | |
| tree | 5b7c3a85ce81bf7b6ac7e51bd64cdfbf476bddb9 | |
| parent | 4fe49d139de02a61c2ad979c3da8c6241e78fdc2 (diff) | |
CSV Exporter (#227)
* CSV Exporter
* CSV Exporter
* updated readme
---------
Co-authored-by: Pavel Zhukov <33721692+LeaveMyYard@users.noreply.github.com>
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | robusta_krr/formatters/__init__.py | 1 | ||||
| -rw-r--r-- | robusta_krr/formatters/csv.py | 111 |
3 files changed, 113 insertions, 0 deletions
@@ -285,6 +285,7 @@ Currently KRR ships with a few formatters to represent the scan data: - `json` - `yaml` - `pprint` - data representation from python's pprint library +- `csv_export` - export data to a csv file in the current directory To run a strategy with a selected formatter, add a `-f` flag: diff --git a/robusta_krr/formatters/__init__.py b/robusta_krr/formatters/__init__.py index 325cf01..e34a25f 100644 --- a/robusta_krr/formatters/__init__.py +++ b/robusta_krr/formatters/__init__.py @@ -2,3 +2,4 @@ from .json import json from .pprint import pprint from .table import table from .yaml import yaml +from .csv import csv diff --git a/robusta_krr/formatters/csv.py b/robusta_krr/formatters/csv.py new file mode 100644 index 0000000..792c061 --- /dev/null +++ b/robusta_krr/formatters/csv.py @@ -0,0 +1,111 @@ +import itertools +import csv + +import logging + + +from robusta_krr.core.abstract import formatters +from robusta_krr.core.models.allocations import RecommendationValue +from robusta_krr.core.models.result import ResourceScan, ResourceType, Result +from robusta_krr.utils import resource_units +import datetime + +logger = logging.getLogger("krr") + +NONE_LITERAL = "unset" +NAN_LITERAL = "?" + + +def _format(value: RecommendationValue) -> str: + if value is None: + return NONE_LITERAL + elif isinstance(value, str): + return NAN_LITERAL + else: + return resource_units.format(value) + + +def __calc_diff(allocated, recommended, selector, multiplier=1) -> str: + if recommended is None or isinstance(recommended.value, str) or selector != "requests": + return "" + else: + reccomended_val = recommended.value if isinstance(recommended.value, (int, float)) else 0 + allocated_val = allocated if isinstance(allocated, (int, float)) else 0 + diff_val = reccomended_val - allocated_val + diff_sign = "+" if diff_val >= 0 else "-" + return f"{diff_sign}{_format(abs(diff_val) * multiplier)}" + + +def _format_request_str(item: ResourceScan, resource: ResourceType, selector: str) -> str: + allocated = getattr(item.object.allocations, selector)[resource] + recommended = getattr(item.recommended, selector)[resource] + + if allocated is None and recommended.value is None: + return f"{NONE_LITERAL}" + + diff = __calc_diff(allocated, recommended, selector) + if diff != "": + diff = f"({diff}) " + + return ( + diff + + _format(allocated) + + " -> " + + _format(recommended.value) + ) + + +def _format_total_diff(item: ResourceScan, resource: ResourceType, pods_current: int) -> str: + selector = "requests" + allocated = getattr(item.object.allocations, selector)[resource] + recommended = getattr(item.recommended, selector)[resource] + + return __calc_diff(allocated, recommended, selector, pods_current) + + +@formatters.register() +def csv_export(result: Result) -> str: + + current_datetime = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + file_path = f"krr-{current_datetime}.csv" + + # We need to order the resource columns so that they are in the format of Namespace,Name,Pods,Old Pods,Type,Container,CPU Diff,CPU Requests,CPU Limits,Memory Diff,Memory Requests,Memory Limits + resource_columns = [] + for resource in ResourceType: + resource_columns.append(f"{resource.name} Diff") + resource_columns.append(f"{resource.name} Requests") + resource_columns.append(f"{resource.name} Limits") + + with open(file_path, 'w+', newline='') as csvfile: + csv_writer = csv.writer(csvfile) + csv_writer.writerow([ + "Namespace", "Name", "Pods", "Old Pods", "Type", "Container", + *resource_columns + + ]) + + for _, group in itertools.groupby( + enumerate(result.scans), key=lambda x: (x[1].object.cluster, x[1].object.namespace, x[1].object.name) + ): + group_items = list(group) + + for j, (i, item) in enumerate(group_items): + full_info_row = j == 0 + + row = [ + item.object.namespace if full_info_row else "", + item.object.name if full_info_row else "", + f"{item.object.current_pods_count}" if full_info_row else "", + f"{item.object.deleted_pods_count}" if full_info_row else "", + item.object.kind if full_info_row else "", + item.object.container, + ] + + for resource in ResourceType: + row.append(_format_total_diff(item, resource, item.object.current_pods_count)) + row += [_format_request_str(item, resource, selector) for selector in ["requests", "limits"]] + + csv_writer.writerow(row) + + logger.info("CSV File: %s", file_path) + return ""
\ No newline at end of file |
