Skip to content

Commit

Permalink
Merge pull request #78 from ImMin5/feature-modify-cost-option
Browse files Browse the repository at this point in the history
Add Saved Cost DataType
  • Loading branch information
ImMin5 authored Aug 1, 2024
2 parents e917055 + fbea446 commit eb5c7f4
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,15 @@ def list_by_billing_account(self):
billing_account_name=billing_account_name
)

def get_retail_price(self, meter_id: str):
url = f"https://prices.azure.com/api/retail/prices?$filter=priceType eq 'Consumption' and meterId eq '{meter_id}'"
try:
response = requests.get(url=url)
return response.json()
except Exception as e:
_LOGGER.error(f"[ERROR] get_retail_price {e}")
raise ERROR_UNKNOWN(message=f"[ERROR] get_retail_price failed {e}")

def _make_request_headers(self, client_type=None):
access_token = self._get_access_token()
headers = {
Expand Down
155 changes: 100 additions & 55 deletions src/cloudforet/cost_analysis/manager/cost_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ def __init__(self, *args, **kwargs):
self.azure_cm_connector: AzureCostMgmtConnector = self.locator.get_connector(
"AzureCostMgmtConnector"
)
self.retail_price_map = {}

def get_linked_accounts(
self,
options: dict,
secret_data: dict,
schema: str,
domain_id: str,
self,
options: dict,
secret_data: dict,
schema: str,
domain_id: str,
) -> dict:
self.azure_cm_connector.create_session(options, secret_data, schema)
billing_account_info = self.azure_cm_connector.get_billing_account()
Expand All @@ -56,12 +57,12 @@ def get_linked_accounts(
return {"results": accounts_info}

def get_benefit_data(
self,
options: dict,
secret_data: dict,
schema: str,
task_options: dict,
domain_id: str,
self,
options: dict,
secret_data: dict,
schema: str,
task_options: dict,
domain_id: str,
):
self.azure_cm_connector.create_session(options, secret_data, schema)
start: datetime = self._get_first_date_of_month(task_options["start"])
Expand All @@ -84,12 +85,12 @@ def get_benefit_data(
)

def _make_benefit_cost_data(
self,
results: dict,
end: datetime,
options: dict,
tenant_id: str = None,
agreement_type: str = None,
self,
results: dict,
end: datetime,
options: dict,
tenant_id: str = None,
agreement_type: str = None,
) -> list:
benefit_costs_data = []
try:
Expand Down Expand Up @@ -142,12 +143,12 @@ def _make_benefit_cost_info(self, result: dict, billed_at: str) -> dict:
return data

def get_data(
self,
options: dict,
secret_data: dict,
schema: str,
task_options: dict,
domain_id: str,
self,
options: dict,
secret_data: dict,
schema: str,
task_options: dict,
domain_id: str,
) -> list:
self.azure_cm_connector.create_session(options, secret_data, schema)
self._check_task_options(task_options)
Expand Down Expand Up @@ -201,12 +202,12 @@ def get_data(
yield []

def _make_cost_data(
self,
results: list,
end: datetime,
options: dict,
tenant_id: str = None,
agreement_type: str = None,
self,
results: list,
end: datetime,
options: dict,
tenant_id: str = None,
agreement_type: str = None,
) -> list:
"""Source Data Model"""

Expand Down Expand Up @@ -247,14 +248,14 @@ def _make_transaction_cost_data(self, tenant_id: str, end: datetime) -> list:

try:
for (
reservation_transaction
reservation_transaction
) in self.azure_cm_connector.list_reservation_transactions_by_billing_profile_id(
query_filter
):

if (
reservation_transaction.invoice_section_id.split("/")[-1]
== invoice_section_id
reservation_transaction.invoice_section_id.split("/")[-1]
== invoice_section_id
):
reservation_transaction_info = (
self.azure_cm_connector.convert_nested_dictionary(
Expand Down Expand Up @@ -311,12 +312,12 @@ def _make_transaction_cost_data(self, tenant_id: str, end: datetime) -> list:
return transaction_cost_data

def _make_data_info(
self,
result: dict,
billed_date: str,
options: dict,
tenant_id: str = None,
agreement_type: str = None,
self,
result: dict,
billed_date: str,
options: dict,
tenant_id: str = None,
agreement_type: str = None,
):
additional_info: dict = self._get_additional_info(result, options, tenant_id)
cost: float = self._get_cost_from_result_with_options(result, options)
Expand Down Expand Up @@ -390,8 +391,8 @@ def _get_additional_info(self, result: dict, options: dict, tenant_id: str = Non
additional_info["Benefit Name"] = benefit_name

if (
result.get("pricingmodel") == "Reservation"
and result["metercategory"] == ""
result.get("pricingmodel") == "Reservation"
and result["metercategory"] == ""
):
result["metercategory"] = self._set_product_from_benefit_name(
benefit_name
Expand All @@ -403,14 +404,14 @@ def _get_additional_info(self, result: dict, options: dict, tenant_id: str = Non
if result.get("metersubcategory") != "" and result.get("metersubcategory"):
additional_info["Meter SubCategory"] = result.get("metersubcategory")
if (
result.get("pricingmodel") == "OnDemand"
and result.get("metercategory") == ""
result.get("pricingmodel") == "OnDemand"
and result.get("metercategory") == ""
):
result["metercategory"] = result.get("metercategory")

if result.get("customername") is None:
if result.get("invoicesectionname") != "" and result.get(
"invoicesectionname"
"invoicesectionname"
):
additional_info["Department Name"] = result.get("invoicesectionname")
elif result.get("departmentname") != "" and result.get("departmentname"):
Expand All @@ -419,7 +420,7 @@ def _get_additional_info(self, result: dict, options: dict, tenant_id: str = Non
if result.get("accountname") != "" and result.get("accountname"):
additional_info["Enrollment Account Name"] = result["accountname"]
elif result.get("enrollmentaccountname") != "" and result.get(
"enrollmentaccountname"
"enrollmentaccountname"
):
additional_info["Enrollment Account Name"] = result["enrollmentaccountname"]

Expand All @@ -428,9 +429,9 @@ def _get_additional_info(self, result: dict, options: dict, tenant_id: str = Non

collect_resource_id = options.get("collect_resource_id", False)
if (
collect_resource_id
and result.get("resourceid") != ""
and result.get("resourceid")
collect_resource_id
and result.get("resourceid") != ""
and result.get("resourceid")
):
additional_info["Resource Id"] = result["resourceid"]
additional_info["Resource Name"] = result["resourceid"].split("/")[-1]
Expand Down Expand Up @@ -488,14 +489,58 @@ def _get_aggregate_data(self, result: dict, options: dict) -> dict:

if result.get("reservationname") != "" and result.get("reservationname"):
aggregate_data["Actual Cost"] = 0
elif result.get("benefitname") != "" and result.get("benefitname"):
aggregate_data["Actual Cost"] = 0
else:
aggregate_data["Actual Cost"] = cost_in_billing_currency

if result.get("pricingmodel") in ["Reservation", "SavingsPlan"]:
aggregate_data["Saved Cost"] = self._get_saved_cost(
result, cost_in_billing_currency
)

else:
aggregate_data["Actual Cost"] = cost_in_billing_currency

return aggregate_data

def _get_saved_cost(self, result: dict, cost: float) -> float:
exchange_rate = 1.0
saved_cost = 0
currency = result.get("billingcurrency", "USD")
meter_id = result.get("meterid")
quantity = self._convert_str_to_float_format(result.get("quantity", 0.0))

if self.retail_price_map.get(meter_id):
unit_price = self.retail_price_map[meter_id]
else:
unit_price = self._get_unit_price_from_meter_id(meter_id)
self.retail_price_map[meter_id] = unit_price

if currency != "USD":
exchange_rate = result.get("exchangeratepricingtobilling", 1.0) or 1.0

retail_cost = exchange_rate * quantity * unit_price
if retail_cost:
saved_cost = retail_cost - cost

return saved_cost

def _get_unit_price_from_meter_id(self, meter_id: str) -> float:
unit_price = 0.0
try:
response = self.azure_cm_connector.get_retail_price(meter_id)
items = response.get("Items", [])

for item in items:
if item.get("meterId") == meter_id:
unit_price = item.get("retailPrice", 0.0)
break

except Exception as e:
_LOGGER.error(f"[_get_unit_price_from_meter_id] get unit price error: {e}")
return unit_price

@staticmethod
def _get_region_code(resource_location: str) -> str:
return resource_location.lower() if resource_location else resource_location
Expand Down Expand Up @@ -524,10 +569,10 @@ def _get_tenant_ids(task_options: dict, collect_scope: str) -> list:

@staticmethod
def _make_scope(
secret_data: dict,
task_options: dict,
collect_scope: str,
customer_tenant_id: str = None,
secret_data: dict,
task_options: dict,
collect_scope: str,
customer_tenant_id: str = None,
):
if collect_scope == "subscription_id":
subscription_id = task_options["subscription_id"]
Expand Down Expand Up @@ -628,7 +673,7 @@ def _convert_date_format_to_utc(date_format: str) -> datetime:
return datetime.strptime(date_format, "%Y-%m-%d").replace(tzinfo=timezone.utc)

def _make_monthly_time_period(
self, start_date: datetime, end_date: datetime
self, start_date: datetime, end_date: datetime
) -> list:
monthly_time_period = []
current_date = end_date
Expand Down Expand Up @@ -658,7 +703,7 @@ def _make_monthly_time_period(

@staticmethod
def _get_linked_customer_tenants(
secret_data: dict, billing_accounts_info: list
secret_data: dict, billing_accounts_info: list
) -> list:
customer_tenants = secret_data.get("customer_tenants", [])
if not customer_tenants:
Expand All @@ -671,7 +716,7 @@ def _get_linked_customer_tenants(

@staticmethod
def _make_accounts_info_from_customer_tenants(
billing_accounts_info: list, customer_tenants: list
billing_accounts_info: list, customer_tenants: list
) -> list:
accounts_info = []
for billing_account_info in billing_accounts_info:
Expand Down Expand Up @@ -709,7 +754,7 @@ def _exclude_cost_data_with_options(result: dict, options: dict) -> bool:

@staticmethod
def _set_network_traffic_cost(
additional_info: dict, product: str, usage_type: str
additional_info: dict, product: str, usage_type: str
) -> dict:
if product in ["Bandwidth", "Content Delivery Network"]:
additional_info["Usage Type Details"] = usage_type
Expand Down

0 comments on commit eb5c7f4

Please sign in to comment.