Skip to content

Add Saved Cost DataType #78

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

Merged
merged 1 commit into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
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
Loading