Skip to content

Commit

Permalink
Add mechanism to ignore some time ranges from a pod's runtime.
Browse files Browse the repository at this point in the history
  • Loading branch information
naved001 committed Oct 21, 2024
1 parent e1d551c commit 79532e1
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 3 deletions.
24 changes: 21 additions & 3 deletions openshift_metrics/invoice.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import math
from dataclasses import dataclass, field
from collections import namedtuple
from typing import List
from typing import List, Tuple
from decimal import Decimal, ROUND_HALF_UP
import datetime

Expand Down Expand Up @@ -112,9 +112,27 @@ def get_service_unit(self) -> ServiceUnit:

return ServiceUnit(su_type, su_count, determining_resource)

def get_runtime(self) -> Decimal:
def get_runtime(
self, ignore_times: List[Tuple[datetime.datetime, datetime.datetime]] = None
) -> Decimal:
"""Return runtime eligible for billing in hours"""
return Decimal(self.duration) / 3600

total_runtime = self.duration
end_time = self.start_time + self.duration

if ignore_times:
for ignore_start_date, ignore_end_date in ignore_times:
ignore_start = int(ignore_start_date.timestamp())
ignore_end = int(ignore_end_date.timestamp())
if ignore_end <= self.start_time or ignore_start >= end_time:
continue
overlap_start = max(self.start_time, ignore_start)
overlap_end = min(end_time, ignore_end)

overlap_duration = max(0, overlap_end - overlap_start)
total_runtime = max(0, total_runtime - overlap_duration)

return Decimal(total_runtime) / 3600

@property
def end_time(self) -> int:
Expand Down
69 changes: 69 additions & 0 deletions openshift_metrics/tests/test_invoice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from unittest import TestCase
from datetime import datetime
from decimal import Decimal

from openshift_metrics import invoice


class TestPodGetRuntime(TestCase):
def setUp(self):
"""Gives us a pod that starts at 2024-10-11 12:00 UTC and ends at 2024-10-11 20:00 UTC"""
self.pod = invoice.Pod(
pod_name="test-pod",
namespace="test-namespace",
start_time=int(datetime(2024, 10, 11, 12, 0).timestamp()),
duration=3600 * 8,
cpu_request=Decimal("1.0"),
gpu_request=Decimal(0),
memory_request=Decimal("4.0"),
gpu_type=None,
gpu_resource=None,
node_hostname="node-1",
node_model=None,
)

def test_no_ignore_times(self):
runtime = self.pod.get_runtime()
self.assertEqual(runtime, Decimal("8.0"))

def test_one_ignore_range(self):
ignore_range = [(datetime(2024, 10, 11, 13, 0), datetime(2024, 10, 11, 14, 0))]
self.assertEqual(self.pod.get_runtime(ignore_range), Decimal(7.0))

def test_multiple_ignore_times(self):
ignore_times = [
(datetime(2024, 10, 11, 13, 0), datetime(2024, 10, 11, 14, 0)),
(datetime(2024, 10, 11, 14, 0), datetime(2024, 10, 11, 15, 0)),
(datetime(2024, 10, 11, 19, 0), datetime(2024, 10, 11, 20, 0)),
]
self.assertEqual(self.pod.get_runtime(ignore_times), Decimal(5.0))

def test_ignore_times_outside_runtime(self):
ignore_times = [
(
datetime(2024, 10, 11, 10, 0),
datetime(2024, 10, 11, 11, 0),
), # before start
(datetime(2024, 10, 11, 20, 0), datetime(2024, 10, 11, 22, 0)), # after end
]
self.assertEqual(self.pod.get_runtime(ignore_times), Decimal(8.0))

def test_partial_overlap_ignore_range(self):
ignore_range = [
(datetime(2024, 10, 11, 10, 30), datetime(2024, 10, 11, 14, 30))
]
self.assertEqual(self.pod.get_runtime(ignore_range), Decimal(5.5))

def test_ignore_range_greater_than_pod_runtime(self):
ignore_range = [
(datetime(2024, 10, 11, 11, 00), datetime(2024, 10, 11, 21, 00))
]
self.assertEqual(self.pod.get_runtime(ignore_range), Decimal(0))

def test_runtime_is_never_negative(self):
ignore_times = [
(datetime(2024, 10, 11, 13, 0), datetime(2024, 10, 11, 17, 0)),
(datetime(2024, 10, 11, 13, 0), datetime(2024, 10, 11, 17, 0)),
(datetime(2024, 10, 11, 10, 0), datetime(2024, 10, 11, 22, 0)),
]
self.assertEqual(self.pod.get_runtime(ignore_times), Decimal(0.0))

0 comments on commit 79532e1

Please sign in to comment.