From af8b460dce3910343dc14bea1a1dafc84940c3e2 Mon Sep 17 00:00:00 2001 From: Naved Ansari Date: Mon, 24 Nov 2025 14:15:19 -0500 Subject: [PATCH 1/3] Add new columns for daily report --- openshift_metrics/invoice.py | 5 ++++- openshift_metrics/merge.py | 25 +++++++++++++++++++------ openshift_metrics/utils.py | 13 +++++++++++-- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/openshift_metrics/invoice.py b/openshift_metrics/invoice.py index 522435d..bbaf850 100644 --- a/openshift_metrics/invoice.py +++ b/openshift_metrics/invoice.py @@ -240,7 +240,7 @@ def get_rate(self, su_type) -> Decimal: return self.rates.gpu_h100 return Decimal(0) - def generate_invoice_rows(self, report_month) -> List[str]: + def generate_invoice_rows(self, report_month, report_start_time, report_end_time, generated_at) -> List[str]: rows = [] for su_type, hours in self.su_hours.items(): if hours > 0: @@ -249,6 +249,8 @@ def generate_invoice_rows(self, report_month) -> List[str]: cost = (rate * hours).quantize(Decimal(".01"), rounding=ROUND_HALF_UP) row = [ report_month, + report_start_time, + report_end_time, self.project, self.project_id, self.pi, @@ -261,6 +263,7 @@ def generate_invoice_rows(self, report_month) -> List[str]: su_type, rate, cost, + generated_at, ] rows.append(row) return rows diff --git a/openshift_metrics/merge.py b/openshift_metrics/merge.py index 0c29364..a0c599e 100644 --- a/openshift_metrics/merge.py +++ b/openshift_metrics/merge.py @@ -5,7 +5,7 @@ import sys import logging import argparse -from datetime import datetime, UTC +from datetime import datetime, UTC, timedelta import json from typing import Tuple from decimal import Decimal @@ -159,9 +159,7 @@ def main(): if cluster_name is None: cluster_name = "Unknown Cluster" - logger.info( - f"Generating report from {report_start_date} to {report_end_date} for {cluster_name}" - ) + logger.info(f"Total metric files read: {len(files)}") report_month = datetime.strftime( datetime.strptime(report_start_date, "%Y-%m-%d"), "%Y-%m" @@ -212,8 +210,12 @@ def main(): else: pod_report_file = f"Pod NERC OpenShift {report_month}.csv" - report_start_date = datetime.strptime(report_start_date, "%Y-%m-%d") - report_end_date = datetime.strptime(report_end_date, "%Y-%m-%d") + report_start_date = datetime.strptime(report_start_date, "%Y-%m-%d").replace(tzinfo=UTC) + report_end_date = datetime.strptime(report_end_date, "%Y-%m-%d").replace(tzinfo=UTC) + timedelta(days=1) + + logger.info( + f"Generating report from {report_start_date} to {report_end_date} for {cluster_name}" + ) if report_start_date.month != report_end_date.month: logger.warning("The report spans multiple months") @@ -224,6 +226,11 @@ def main(): ) su_definitions = get_su_definitions(report_month) + + generated_at = datetime.now(UTC).strftime("%Y-%m-%d%H:%M:%SZ") + report_start_time = report_start_date.strftime("%Y-%m-%d%H:%M:%SZ") + report_end_time = report_end_date.strftime("%Y-%m-%d%H:%M:%SZ") + utils.write_metrics_by_namespace( condensed_metrics_dict=condensed_metrics_dict, file_name=invoice_file, @@ -231,6 +238,9 @@ def main(): rates=invoice_rates, su_definitions=su_definitions, cluster_name=cluster_name, + report_start_time=report_start_time, + report_end_time=report_end_time, + generated_at=generated_at, ignore_hours=ignore_hours, ) utils.write_metrics_by_classes( @@ -241,6 +251,9 @@ def main(): su_definitions=su_definitions, cluster_name=cluster_name, namespaces_with_classes=["rhods-notebooks"], + report_start_time=report_start_time, + report_end_time=report_end_time, + generated_at=generated_at, ignore_hours=ignore_hours, ) utils.write_metrics_by_pod( diff --git a/openshift_metrics/utils.py b/openshift_metrics/utils.py index 6937ae0..4d04c95 100755 --- a/openshift_metrics/utils.py +++ b/openshift_metrics/utils.py @@ -64,6 +64,9 @@ def write_metrics_by_namespace( rates, su_definitions, cluster_name, + report_start_time, + report_end_time, + generated_at, ignore_hours=None, ): """ @@ -73,6 +76,8 @@ def write_metrics_by_namespace( rows = [] headers = [ "Invoice Month", + "Report Start Time", + "Report End Time", "Project - Allocation", "Project - Allocation ID", "Manager (PI)", @@ -85,6 +90,7 @@ def write_metrics_by_namespace( "SU Type", "Rate", "Cost", + "Generated At", ] rows.append(headers) @@ -128,7 +134,7 @@ def write_metrics_by_namespace( project_invoice.add_pod(pod_obj) for project_invoice in invoices.values(): - rows.extend(project_invoice.generate_invoice_rows(report_month)) + rows.extend(project_invoice.generate_invoice_rows(report_month, report_start_time, report_end_time, generated_at)) csv_writer(rows, file_name) @@ -190,6 +196,9 @@ def write_metrics_by_classes( namespaces_with_classes, su_definitions, cluster_name, + report_start_time, + report_end_time, + generated_at, ignore_hours=None, ): """ @@ -265,6 +274,6 @@ def write_metrics_by_classes( project_invoice.add_pod(pod_obj) for project_invoice in invoices.values(): - rows.extend(project_invoice.generate_invoice_rows(report_month)) + rows.extend(project_invoice.generate_invoice_rows(report_month, report_start_time, report_end_time, generated_at)) csv_writer(rows, file_name) From f76b2d2b38228831e5f1e54a3854a00732c3aef8 Mon Sep 17 00:00:00 2001 From: Naved Ansari Date: Mon, 24 Nov 2025 17:16:32 -0500 Subject: [PATCH 2/3] Restructure some things for a project invoice * Remove attributes that we get from coldfront from the invoice. Still keep the CSV output consistent though. * Hold report metadata in a dataclass and pass that around to the helper functions to keep things better contained. --- openshift_metrics/invoice.py | 38 +++++++++++++++++++----------------- openshift_metrics/merge.py | 30 ++++++++++++++-------------- openshift_metrics/utils.py | 30 ++++------------------------ 3 files changed, 39 insertions(+), 59 deletions(-) diff --git a/openshift_metrics/invoice.py b/openshift_metrics/invoice.py index bbaf850..aa69823 100644 --- a/openshift_metrics/invoice.py +++ b/openshift_metrics/invoice.py @@ -192,19 +192,21 @@ class Rates: gpu_h100: Decimal +@dataclass() +class ReportMetadata: + report_month: str + cluster_name: str + report_start_time: datetime.datetime + report_end_time: datetime.datetime + generated_at: datetime.datetime + + @dataclass class ProjectInvoce: """Represents the invoicing data for a project.""" - invoice_month: str project: str project_id: str - pi: str - cluster_name: str - invoice_email: str - invoice_address: str - intitution: str - institution_specific_code: str rates: Rates su_definitions: dict ignore_hours: Optional[List[Tuple[datetime.datetime, datetime.datetime]]] = None @@ -240,7 +242,7 @@ def get_rate(self, su_type) -> Decimal: return self.rates.gpu_h100 return Decimal(0) - def generate_invoice_rows(self, report_month, report_start_time, report_end_time, generated_at) -> List[str]: + def generate_invoice_rows(self, metadata: ReportMetadata) -> List[str]: rows = [] for su_type, hours in self.su_hours.items(): if hours > 0: @@ -248,22 +250,22 @@ def generate_invoice_rows(self, report_month, report_start_time, report_end_time rate = self.get_rate(su_type) cost = (rate * hours).quantize(Decimal(".01"), rounding=ROUND_HALF_UP) row = [ - report_month, - report_start_time, - report_end_time, + metadata.report_month, + metadata.report_start_time.strftime("%Y-%m-%d%H:%M:%SZ"), + metadata.report_end_time.strftime("%Y-%m-%d%H:%M:%SZ"), self.project, self.project_id, - self.pi, - self.cluster_name, - self.invoice_email, - self.invoice_address, - self.intitution, - self.institution_specific_code, + "", # pi + metadata.cluster_name, + "", # invoice_email + "", # invoice_address + "", # institution + "", # institution_specific_code hours, su_type, rate, cost, - generated_at, + metadata.generated_at.strftime("%Y-%m-%d%H:%M:%SZ"), ] rows.append(row) return rows diff --git a/openshift_metrics/merge.py b/openshift_metrics/merge.py index a0c599e..2182fd3 100644 --- a/openshift_metrics/merge.py +++ b/openshift_metrics/merge.py @@ -210,8 +210,12 @@ def main(): else: pod_report_file = f"Pod NERC OpenShift {report_month}.csv" - report_start_date = datetime.strptime(report_start_date, "%Y-%m-%d").replace(tzinfo=UTC) - report_end_date = datetime.strptime(report_end_date, "%Y-%m-%d").replace(tzinfo=UTC) + timedelta(days=1) + report_start_date = datetime.strptime(report_start_date, "%Y-%m-%d").replace( + tzinfo=UTC + ) + report_end_date = datetime.strptime(report_end_date, "%Y-%m-%d").replace( + tzinfo=UTC + ) + timedelta(days=1) logger.info( f"Generating report from {report_start_date} to {report_end_date} for {cluster_name}" @@ -227,33 +231,29 @@ def main(): su_definitions = get_su_definitions(report_month) - generated_at = datetime.now(UTC).strftime("%Y-%m-%d%H:%M:%SZ") - report_start_time = report_start_date.strftime("%Y-%m-%d%H:%M:%SZ") - report_end_time = report_end_date.strftime("%Y-%m-%d%H:%M:%SZ") + report_metadata = invoice.ReportMetadata( + report_month=report_month, + cluster_name=cluster_name, + report_start_time=report_start_date, + report_end_time=report_end_date, + generated_at=datetime.now(UTC), + ) utils.write_metrics_by_namespace( condensed_metrics_dict=condensed_metrics_dict, file_name=invoice_file, - report_month=report_month, + report_metadata=report_metadata, rates=invoice_rates, su_definitions=su_definitions, - cluster_name=cluster_name, - report_start_time=report_start_time, - report_end_time=report_end_time, - generated_at=generated_at, ignore_hours=ignore_hours, ) utils.write_metrics_by_classes( condensed_metrics_dict=condensed_metrics_dict, file_name=class_invoice_file, - report_month=report_month, + report_metadata=report_metadata, rates=invoice_rates, su_definitions=su_definitions, - cluster_name=cluster_name, namespaces_with_classes=["rhods-notebooks"], - report_start_time=report_start_time, - report_end_time=report_end_time, - generated_at=generated_at, ignore_hours=ignore_hours, ) utils.write_metrics_by_pod( diff --git a/openshift_metrics/utils.py b/openshift_metrics/utils.py index 4d04c95..3243247 100755 --- a/openshift_metrics/utils.py +++ b/openshift_metrics/utils.py @@ -60,13 +60,9 @@ def csv_writer(rows, file_name): def write_metrics_by_namespace( condensed_metrics_dict, file_name, - report_month, + report_metadata: invoice.ReportMetadata, rates, su_definitions, - cluster_name, - report_start_time, - report_end_time, - generated_at, ignore_hours=None, ): """ @@ -98,15 +94,8 @@ def write_metrics_by_namespace( for namespace, pods in condensed_metrics_dict.items(): if namespace not in invoices: project_invoice = invoice.ProjectInvoce( - invoice_month=report_month, project=namespace, project_id=namespace, - pi="", - cluster_name=cluster_name, - invoice_email="", - invoice_address="", - intitution="", - institution_specific_code="", rates=rates, su_definitions=su_definitions, ignore_hours=ignore_hours, @@ -134,7 +123,7 @@ def write_metrics_by_namespace( project_invoice.add_pod(pod_obj) for project_invoice in invoices.values(): - rows.extend(project_invoice.generate_invoice_rows(report_month, report_start_time, report_end_time, generated_at)) + rows.extend(project_invoice.generate_invoice_rows(report_metadata)) csv_writer(rows, file_name) @@ -191,14 +180,10 @@ def write_metrics_by_pod( def write_metrics_by_classes( condensed_metrics_dict, file_name, - report_month, + report_metadata: invoice.ReportMetadata, rates, namespaces_with_classes, su_definitions, - cluster_name, - report_start_time, - report_end_time, - generated_at, ignore_hours=None, ): """ @@ -240,15 +225,8 @@ def write_metrics_by_classes( if project_name not in invoices: project_invoice = invoice.ProjectInvoce( - invoice_month=report_month, project=project_name, project_id=project_name, - pi="", - cluster_name=cluster_name, - invoice_email="", - invoice_address="", - intitution="", - institution_specific_code="", su_definitions=su_definitions, rates=rates, ignore_hours=ignore_hours, @@ -274,6 +252,6 @@ def write_metrics_by_classes( project_invoice.add_pod(pod_obj) for project_invoice in invoices.values(): - rows.extend(project_invoice.generate_invoice_rows(report_month, report_start_time, report_end_time, generated_at)) + rows.extend(project_invoice.generate_invoice_rows(report_metadata)) csv_writer(rows, file_name) From 271f32c27d5dc7657c99c7b36ec69e6dfd58819e Mon Sep 17 00:00:00 2001 From: Naved Ansari Date: Tue, 25 Nov 2025 14:16:41 -0500 Subject: [PATCH 3/3] Fix tests Also update the headers for the report by classes, not that we specifically need it for that report, but it's just good to keep it consistent. --- openshift_metrics/tests/test_utils.py | 79 +++++++++++++++++---------- openshift_metrics/utils.py | 3 + 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/openshift_metrics/tests/test_utils.py b/openshift_metrics/tests/test_utils.py index 0cea1c6..d76adf0 100644 --- a/openshift_metrics/tests/test_utils.py +++ b/openshift_metrics/tests/test_utils.py @@ -109,6 +109,15 @@ def test_write_metrics_log(self): class TestWriteMetricsByNamespace(TestCase): + def setUp(self) -> None: + self.report_metadata = invoice.ReportMetadata( + report_month="2023-01", + cluster_name="test-cluster", + report_start_time=datetime(2023, 1, 1, tzinfo=UTC), + report_end_time=datetime(2023, 1, 3, tzinfo=UTC), + generated_at=datetime(2023, 1, 5, tzinfo=UTC), + ) + def test_write_metrics_log(self): test_metrics_dict = { "namespace1": { @@ -180,21 +189,20 @@ def test_write_metrics_log(self): } expected_output = ( - "Invoice Month,Project - Allocation,Project - Allocation ID,Manager (PI),Cluster Name,Invoice Email,Invoice Address,Institution,Institution - Specific Code,SU Hours (GBhr or SUhr),SU Type,Rate,Cost\n" - "2023-01,namespace1,namespace1,,test-cluster,,,,,1128,OpenShift CPU,0.013,14.66\n" - "2023-01,namespace2,namespace2,,test-cluster,,,,,96,OpenShift CPU,0.013,1.25\n" - "2023-01,namespace2,namespace2,,test-cluster,,,,,48,OpenShift GPUA100,1.803,86.54\n" - "2023-01,namespace2,namespace2,,test-cluster,,,,,48,OpenShift GPUA100SXM4,2.078,99.74\n" + "Invoice Month,Report Start Time,Report End Time,Project - Allocation,Project - Allocation ID,Manager (PI),Cluster Name,Invoice Email,Invoice Address,Institution,Institution - Specific Code,SU Hours (GBhr or SUhr),SU Type,Rate,Cost,Generated At\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace1,namespace1,,test-cluster,,,,,1128,OpenShift CPU,0.013,14.66,2023-01-0500:00:00Z\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace2,namespace2,,test-cluster,,,,,96,OpenShift CPU,0.013,1.25,2023-01-0500:00:00Z\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace2,namespace2,,test-cluster,,,,,48,OpenShift GPUA100,1.803,86.54,2023-01-0500:00:00Z\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace2,namespace2,,test-cluster,,,,,48,OpenShift GPUA100SXM4,2.078,99.74,2023-01-0500:00:00Z\n" ) with tempfile.NamedTemporaryFile(mode="w+") as tmp: utils.write_metrics_by_namespace( condensed_metrics_dict=test_metrics_dict, file_name=tmp.name, - report_month="2023-01", + report_metadata=self.report_metadata, rates=RATES, su_definitions=SU_DEFINITIONS, - cluster_name="test-cluster", ) self.assertEqual(tmp.read(), expected_output) @@ -229,24 +237,32 @@ def test_write_metrics_for_vms(self): } expected_output = ( - "Invoice Month,Project - Allocation,Project - Allocation ID,Manager (PI),Cluster Name,Invoice Email,Invoice Address,Institution,Institution - Specific Code,SU Hours (GBhr or SUhr),SU Type,Rate,Cost\n" - "2023-01,namespace1,namespace1,,test-cluster,,,,,24,OpenShift GPUA100SXM4,2.078,49.87\n" - "2023-01,namespace1,namespace1,,test-cluster,,,,,24,OpenShift GPUH100,6.04,144.96\n" + "Invoice Month,Report Start Time,Report End Time,Project - Allocation,Project - Allocation ID,Manager (PI),Cluster Name,Invoice Email,Invoice Address,Institution,Institution - Specific Code,SU Hours (GBhr or SUhr),SU Type,Rate,Cost,Generated At\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace1,namespace1,,test-cluster,,,,,24,OpenShift GPUA100SXM4,2.078,49.87,2023-01-0500:00:00Z\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace1,namespace1,,test-cluster,,,,,24,OpenShift GPUH100,6.04,144.96,2023-01-0500:00:00Z\n" ) with tempfile.NamedTemporaryFile(mode="w+") as tmp: utils.write_metrics_by_namespace( condensed_metrics_dict=test_metrics_dict, file_name=tmp.name, - report_month="2023-01", + report_metadata=self.report_metadata, rates=RATES, su_definitions=SU_DEFINITIONS, - cluster_name="test-cluster", ) self.assertEqual(tmp.read(), expected_output) class TestWriteMetricsByClasses(TestCase): + def setUp(self) -> None: + self.report_metadata = invoice.ReportMetadata( + report_month="2023-01", + cluster_name="test-cluster", + report_start_time=datetime(2023, 1, 1, tzinfo=UTC), + report_end_time=datetime(2023, 1, 3, tzinfo=UTC), + generated_at=datetime(2023, 1, 5, tzinfo=UTC), + ) + def test_write_metrics_log(self): test_metrics_dict = { "namespace1": { # namespace is ignored entirely from the report @@ -321,22 +337,21 @@ def test_write_metrics_log(self): } expected_output = ( - "Invoice Month,Project - Allocation,Project - Allocation ID,Manager (PI),Cluster Name,Invoice Email,Invoice Address,Institution,Institution - Specific Code,SU Hours (GBhr or SUhr),SU Type,Rate,Cost\n" - "2023-01,namespace2:noclass,namespace2:noclass,,test-cluster,,,,,96,OpenShift CPU,0.013,1.25\n" - "2023-01,namespace2:math-201,namespace2:math-201,,test-cluster,,,,,96,OpenShift CPU,0.013,1.25\n" - "2023-01,namespace2:math-201,namespace2:math-201,,test-cluster,,,,,24,OpenShift GPUA100,1.803,43.27\n" - "2023-01,namespace2:cs-101,namespace2:cs-101,,test-cluster,,,,,48,OpenShift GPUA100SXM4,2.078,99.74\n" + "Invoice Month,Report Start Time,Report End Time,Project - Allocation,Project - Allocation ID,Manager (PI),Cluster Name,Invoice Email,Invoice Address,Institution,Institution - Specific Code,SU Hours (GBhr or SUhr),SU Type,Rate,Cost,Generated At\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace2:noclass,namespace2:noclass,,test-cluster,,,,,96,OpenShift CPU,0.013,1.25,2023-01-0500:00:00Z\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace2:math-201,namespace2:math-201,,test-cluster,,,,,96,OpenShift CPU,0.013,1.25,2023-01-0500:00:00Z\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace2:math-201,namespace2:math-201,,test-cluster,,,,,24,OpenShift GPUA100,1.803,43.27,2023-01-0500:00:00Z\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace2:cs-101,namespace2:cs-101,,test-cluster,,,,,48,OpenShift GPUA100SXM4,2.078,99.74,2023-01-0500:00:00Z\n" ) with tempfile.NamedTemporaryFile(mode="w+") as tmp: utils.write_metrics_by_classes( condensed_metrics_dict=test_metrics_dict, file_name=tmp.name, - report_month="2023-01", + report_metadata=self.report_metadata, rates=RATES, su_definitions=SU_DEFINITIONS, namespaces_with_classes=["namespace2"], - cluster_name="test-cluster", ) self.assertEqual(tmp.read(), expected_output) @@ -370,18 +385,17 @@ def test_write_metrics_by_namespace_decimal(self): self.assertEqual(cost, 0.45) expected_output = ( - "Invoice Month,Project - Allocation,Project - Allocation ID,Manager (PI),Cluster Name,Invoice Email,Invoice Address,Institution,Institution - Specific Code,SU Hours (GBhr or SUhr),SU Type,Rate,Cost\n" - "2023-01,namespace1,namespace1,,test-cluster,,,,,35,OpenShift CPU,0.013,0.46\n" + "Invoice Month,Report Start Time,Report End Time,Project - Allocation,Project - Allocation ID,Manager (PI),Cluster Name,Invoice Email,Invoice Address,Institution,Institution - Specific Code,SU Hours (GBhr or SUhr),SU Type,Rate,Cost,Generated At\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace1,namespace1,,test-cluster,,,,,35,OpenShift CPU,0.013,0.46,2023-01-0500:00:00Z\n" ) with tempfile.NamedTemporaryFile(mode="w+") as tmp: utils.write_metrics_by_namespace( condensed_metrics_dict=test_metrics_dict, file_name=tmp.name, - report_month="2023-01", + report_metadata=self.report_metadata, su_definitions=SU_DEFINITIONS, rates=RATES, - cluster_name="test-cluster", ) self.assertEqual(tmp.read(), expected_output) @@ -447,22 +461,29 @@ def setUp(self): }, } + self.report_metadata = invoice.ReportMetadata( + report_month="2023-01", + cluster_name="test-cluster", + report_start_time=datetime(2023, 1, 1, tzinfo=UTC), + report_end_time=datetime(2023, 1, 3, tzinfo=UTC), + generated_at=datetime(2023, 1, 5, tzinfo=UTC), + ) + def test_write_metrics_by_namespace_with_ignore_hours(self): expected_output = ( - "Invoice Month,Project - Allocation,Project - Allocation ID,Manager (PI),Cluster Name,Invoice Email,Invoice Address,Institution,Institution - Specific Code,SU Hours (GBhr or SUhr),SU Type,Rate,Cost\n" - "2023-01,namespace1,namespace1,,test-cluster,,,,,12,OpenShift CPU,0.013,0.16\n" - "2023-01,namespace2,namespace2,,test-cluster,,,,,170,OpenShift CPU,0.013,2.21\n" - "2023-01,namespace2,namespace2,,test-cluster,,,,,37,OpenShift GPUA100SXM4,2.078,76.89\n" + "Invoice Month,Report Start Time,Report End Time,Project - Allocation,Project - Allocation ID,Manager (PI),Cluster Name,Invoice Email,Invoice Address,Institution,Institution - Specific Code,SU Hours (GBhr or SUhr),SU Type,Rate,Cost,Generated At\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace1,namespace1,,test-cluster,,,,,12,OpenShift CPU,0.013,0.16,2023-01-0500:00:00Z\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace2,namespace2,,test-cluster,,,,,170,OpenShift CPU,0.013,2.21,2023-01-0500:00:00Z\n" + "2023-01,2023-01-0100:00:00Z,2023-01-0300:00:00Z,namespace2,namespace2,,test-cluster,,,,,37,OpenShift GPUA100SXM4,2.078,76.89,2023-01-0500:00:00Z\n" ) with tempfile.NamedTemporaryFile(mode="w+") as tmp: utils.write_metrics_by_namespace( condensed_metrics_dict=self.test_metrics_dict, file_name=tmp.name, - report_month="2023-01", + report_metadata=self.report_metadata, rates=RATES, su_definitions=SU_DEFINITIONS, - cluster_name="test-cluster", ignore_hours=self.ignore_times, ) self.assertEqual(tmp.read(), expected_output) diff --git a/openshift_metrics/utils.py b/openshift_metrics/utils.py index 3243247..6a0e4c4 100755 --- a/openshift_metrics/utils.py +++ b/openshift_metrics/utils.py @@ -196,6 +196,8 @@ def write_metrics_by_classes( rows = [] headers = [ "Invoice Month", + "Report Start Time", + "Report End Time", "Project - Allocation", "Project - Allocation ID", "Manager (PI)", @@ -208,6 +210,7 @@ def write_metrics_by_classes( "SU Type", "Rate", "Cost", + "Generated At", ] rows.append(headers)