Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for not billing during outages #68

Merged
merged 2 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions src/openstack_billing_db/billing.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@
import os

from openstack_billing_db import model
from openstack_billing_db import utils

import boto3

logger = logging.getLogger(__name__)

# FIXME(knikolla): Temporarily hardcoding the days of outage here
# until we have formalized how to store them in the nerc-rates repo.
# Usage during these intervals is subtracted from the usage during
# the month.
OUTAGES_FOR_MONTH = {"2024-05": [("2024-05-22", "2024-05-29")]}


@dataclass()
class Rates(object):
Expand Down Expand Up @@ -81,7 +88,9 @@ def gpu_a2_su_cost(self) -> Decimal:
return self.rates.gpu_a2 * self.gpu_a2_su_hours


def collect_invoice_data_from_openstack(database, billing_start, billing_end, rates):
def collect_invoice_data_from_openstack(
database, billing_start, billing_end, rates, invoice_month=None
):
invoices = []
for project in database.projects:
invoice = ProjectInvoice(
Expand All @@ -96,6 +105,14 @@ def collect_invoice_data_from_openstack(database, billing_start, billing_end, ra

for i in project.instances: # type: model.Instance
runtime = i.get_runtime_during(billing_start, billing_end)

if invoice_month and invoice_month in OUTAGES_FOR_MONTH:
for interval in OUTAGES_FOR_MONTH[invoice_month]:
runtime -= i.get_runtime_during(
start_time=utils.parse_time_from_string(interval[0]),
end_time=utils.parse_time_from_string(interval[1]),
)

runtime_seconds = runtime.total_seconds_running
if rates.include_stopped_runtime:
runtime_seconds += runtime.total_seconds_stopped
Expand Down Expand Up @@ -221,7 +238,9 @@ def generate_billing(
):
database = model.Database(start, sql_dump_file)

invoices = collect_invoice_data_from_openstack(database, start, end, rates)
invoices = collect_invoice_data_from_openstack(
database, start, end, rates, invoice_month=invoice_month
)
if coldfront_data_file:
merge_coldfront_data(invoices, coldfront_data_file)
write(invoices, output, invoice_month)
Expand Down
4 changes: 2 additions & 2 deletions src/openstack_billing_db/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
import argparse
import logging

from openstack_billing_db import billing, fetch
from openstack_billing_db import billing, fetch, utils

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


def parse_time_argument(arg):
if isinstance(arg, str):
return datetime.strptime(arg, "%Y-%m-%d")
return utils.parse_time_from_string(arg)
return arg


Expand Down
6 changes: 6 additions & 0 deletions src/openstack_billing_db/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ class InstanceRuntime(object):
total_seconds_running: int = 0
total_seconds_stopped: int = 0

def __sub__(self, other):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

learnt something new!

return InstanceRuntime(
self.total_seconds_running - other.total_seconds_running,
self.total_seconds_stopped - other.total_seconds_stopped,
)


@dataclass
class Instance(object):
Expand Down
11 changes: 11 additions & 0 deletions src/openstack_billing_db/tests/unit/test_instance_runtime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from openstack_billing_db.model import InstanceRuntime


def test_instance_runtime_subtract():
a = InstanceRuntime(total_seconds_running=1000, total_seconds_stopped=1000)
b = InstanceRuntime(total_seconds_running=100, total_seconds_stopped=200)
c = a - b
assert c.total_seconds_running == 900
assert c.total_seconds_running == a.total_seconds_running - b.total_seconds_running
assert c.total_seconds_stopped == 800
assert c.total_seconds_stopped == a.total_seconds_stopped - b.total_seconds_stopped
5 changes: 5 additions & 0 deletions src/openstack_billing_db/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from datetime import datetime


def parse_time_from_string(time_str: str) -> datetime:
return datetime.strptime(time_str, "%Y-%m-%d")
Loading