Skip to content

Commit

Permalink
Add AWS Savings Plan Negation (#524)
Browse files Browse the repository at this point in the history
* Add AWS Savings Plan Negation options
  • Loading branch information
lcouzens authored Aug 13, 2024
1 parent c87c693 commit 31ad6f3
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 8 deletions.
2 changes: 1 addition & 1 deletion nise/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__version__ = "4.6.5"
__version__ = "4.6.6"

VERSION = __version__.split(".")
17 changes: 14 additions & 3 deletions nise/generators/aws/data_transfer_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(self, start_date, end_date, currency, payer_account, usage_accounts
self._rate = float(self.attributes.get("rate", 0)) or None
self._resource_id = f"i-{self.attributes.get('resource_id', self.fake.ean8())}"
self._saving = float(self.attributes.get("saving", 0)) or None
self._negation = self.attributes.get("negation") or False
self._tags = self.attributes.get("tags", self._tags)

@property
Expand Down Expand Up @@ -81,7 +82,8 @@ def _update_data(self, row, start, end, **kwargs):

resource_id = self._resource_id if self._resource_id else self.fake.ean8()
rate = self._rate if self._rate else round(uniform(0.12, 0.19), 3)
saving = self._saving if self._saving else round(uniform(0.12, 0.19), 3)
saving = self._saving
negation = self._negation
amount = self._amount if self._amount else uniform(0.000002, 0.09)
cost = amount * rate
trans_desc, operation, description, location1, location2, trans_type, aws_region = self._get_data_transfer(
Expand Down Expand Up @@ -118,8 +120,17 @@ def _update_data(self, row, start, end, **kwargs):
# Overwrite lineItem/LineItemType for items with applied Savings plan
if saving is not None:
row["lineItem/LineItemType"] = "SavingsPlanCoveredUsage"
self._add_tag_data(row)
self._add_category_data(row)

if negation:
row["lineItem/LineItemType"] = "SavingsPlanNegation"
row["lineItem/UnblendedCost"] = -abs(cost)
row["lineItem/LineItemDescription"] = f"SavingsPlanNegation used by AccountId : {self.payer_account}"
row["lineItem/ResourceId"] = None
row["lineItem/BlendedCost"] = -abs(cost)

if not negation:
self._add_tag_data(row)
self._add_category_data(row)
return row

def generate_data(self, report_type=None):
Expand Down
38 changes: 34 additions & 4 deletions nise/generators/aws/ec2_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class EC2Generator(AWSGenerator):
"0.096",
"0.096",
"0.045",
1,
False,
"${cost} per On Demand Linux {inst_type} Instance Hour",
),
(
Expand All @@ -48,6 +50,8 @@ class EC2Generator(AWSGenerator):
"0.34",
"0.34",
"0.17",
1,
False,
"${cost} per On Demand Linux {inst_type} Instance Hour",
),
(
Expand All @@ -60,6 +64,8 @@ class EC2Generator(AWSGenerator):
"0.199",
"0.199",
"0.099",
1,
False,
"${cost} per On Demand Linux {inst_type} Instance Hour",
),
(
Expand All @@ -72,6 +78,8 @@ class EC2Generator(AWSGenerator):
"0.133",
"0.133",
"0.067",
1,
False,
"${cost} per On Demand Linux {inst_type} Instance Hour",
),
)
Expand Down Expand Up @@ -115,12 +123,27 @@ def __init__(self, start_date, end_date, currency, payer_account, usage_accounts
instance_type.get("cost"),
instance_type.get("rate"),
instance_type.get("saving"),
instance_type.get("amount", "1"),
instance_type.get("negation", False),
"${cost} per On Demand Linux {inst_type} Instance Hour",
)

def _update_data(self, row, start, end, **kwargs):
"""Update data with generator specific data."""
inst_type, physical_cores, vcpu, memory, storage, family, cost, rate, saving, description = self._instance_type
(
inst_type,
physical_cores,
vcpu,
memory,
storage,
family,
cost,
rate,
saving,
amount,
negation,
description,
) = self._instance_type

inst_description = description.format(cost=cost, inst_type=inst_type)
product_name = "Amazon Elastic Compute Cloud"
Expand All @@ -137,7 +160,7 @@ def _update_data(self, row, start, end, **kwargs):
row["lineItem/Operation"] = "RunInstances"
row["lineItem/AvailabilityZone"] = avail_zone
row["lineItem/ResourceId"] = self._resource_id
row["lineItem/UsageAmount"] = "1"
row["lineItem/UsageAmount"] = amount
row["lineItem/UnblendedRate"] = rate
row["lineItem/UnblendedCost"] = cost
row["lineItem/BlendedRate"] = rate
Expand Down Expand Up @@ -179,9 +202,16 @@ def _update_data(self, row, start, end, **kwargs):
# Overwrite lineItem/LineItemType for items with applied Savings plan
if saving is not None:
row["lineItem/LineItemType"] = "SavingsPlanCoveredUsage"
if negation:
row["lineItem/LineItemType"] = "SavingsPlanNegation"
row["lineItem/UnblendedCost"] = -abs(cost)
row["lineItem/LineItemDescription"] = f"SavingsPlanNegation used by AccountId : {self.payer_account}"
row["lineItem/ResourceId"] = None
row["lineItem/BlendedCost"] = -abs(cost)

self._add_tag_data(row)
self._add_category_data(row)
if not negation:
self._add_tag_data(row)
self._add_category_data(row)
return row

def generate_data(self, report_type=None):
Expand Down
29 changes: 29 additions & 0 deletions tests/test_aws_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,21 @@ def test_update_data(self):
self.assertEqual(row["product/productFamily"], "Data Transfer")
self.assertEqual(row[self.cost_category_key], self.cost_category_value)

def test_update_data_transfer_negation(self):
"""Test DataTransfer specific update data with negation costs."""
self.attributes = {
"rate": 20,
"amount": 1,
"negation": True,
}
generator = DataTransferGenerator(
self.two_hours_ago, self.now, self.currency, self.payer_account, self.usage_accounts, self.attributes
)
start_row = {}
row = generator._update_data(start_row, self.two_hours_ago, self.now)

self.assertEqual(row["lineItem/LineItemType"], "SavingsPlanNegation")


class TestEBSGenerator(AWSGeneratorTestCase):
"""Tests for the EBS Generator type."""
Expand Down Expand Up @@ -377,6 +392,8 @@ def test_init_with_attributes(self):
"cost": "1",
"rate": "1",
"saving": "1",
"amount": 1,
"negation": False,
}
self.attributes["instance_type"] = self.instance_type

Expand All @@ -388,6 +405,18 @@ def test_init_with_attributes(self):
self.assertEqual(generator._resource_id, "i-" + self.resource_id)
self.assertEqual(generator._instance_type[:-1], tuple(self.instance_type.values()))

def test_update_data_ec2_negation(self):
"""Test EC2 specific update data with negation costs."""
self.instance_type = {"negation": True, "cost": 10}
self.attributes["instance_type"] = self.instance_type
generator = EC2Generator(
self.two_hours_ago, self.now, self.currency, self.payer_account, self.usage_accounts, self.attributes
)
start_row = {}
row = generator._update_data(start_row, self.two_hours_ago, self.now)

self.assertEqual(row["lineItem/LineItemType"], "SavingsPlanNegation")

def test_update_data(self):
"""Test EBS specific update data method."""
generator = EC2Generator(
Expand Down

0 comments on commit 31ad6f3

Please sign in to comment.