@@ -23,13 +23,14 @@ def __init__(self, *args, **kwargs):
23
23
self .azure_cm_connector : AzureCostMgmtConnector = self .locator .get_connector (
24
24
"AzureCostMgmtConnector"
25
25
)
26
+ self .retail_price_map = {}
26
27
27
28
def get_linked_accounts (
28
- self ,
29
- options : dict ,
30
- secret_data : dict ,
31
- schema : str ,
32
- domain_id : str ,
29
+ self ,
30
+ options : dict ,
31
+ secret_data : dict ,
32
+ schema : str ,
33
+ domain_id : str ,
33
34
) -> dict :
34
35
self .azure_cm_connector .create_session (options , secret_data , schema )
35
36
billing_account_info = self .azure_cm_connector .get_billing_account ()
@@ -56,12 +57,12 @@ def get_linked_accounts(
56
57
return {"results" : accounts_info }
57
58
58
59
def get_benefit_data (
59
- self ,
60
- options : dict ,
61
- secret_data : dict ,
62
- schema : str ,
63
- task_options : dict ,
64
- domain_id : str ,
60
+ self ,
61
+ options : dict ,
62
+ secret_data : dict ,
63
+ schema : str ,
64
+ task_options : dict ,
65
+ domain_id : str ,
65
66
):
66
67
self .azure_cm_connector .create_session (options , secret_data , schema )
67
68
start : datetime = self ._get_first_date_of_month (task_options ["start" ])
@@ -84,12 +85,12 @@ def get_benefit_data(
84
85
)
85
86
86
87
def _make_benefit_cost_data (
87
- self ,
88
- results : dict ,
89
- end : datetime ,
90
- options : dict ,
91
- tenant_id : str = None ,
92
- agreement_type : str = None ,
88
+ self ,
89
+ results : dict ,
90
+ end : datetime ,
91
+ options : dict ,
92
+ tenant_id : str = None ,
93
+ agreement_type : str = None ,
93
94
) -> list :
94
95
benefit_costs_data = []
95
96
try :
@@ -142,12 +143,12 @@ def _make_benefit_cost_info(self, result: dict, billed_at: str) -> dict:
142
143
return data
143
144
144
145
def get_data (
145
- self ,
146
- options : dict ,
147
- secret_data : dict ,
148
- schema : str ,
149
- task_options : dict ,
150
- domain_id : str ,
146
+ self ,
147
+ options : dict ,
148
+ secret_data : dict ,
149
+ schema : str ,
150
+ task_options : dict ,
151
+ domain_id : str ,
151
152
) -> list :
152
153
self .azure_cm_connector .create_session (options , secret_data , schema )
153
154
self ._check_task_options (task_options )
@@ -201,12 +202,12 @@ def get_data(
201
202
yield []
202
203
203
204
def _make_cost_data (
204
- self ,
205
- results : list ,
206
- end : datetime ,
207
- options : dict ,
208
- tenant_id : str = None ,
209
- agreement_type : str = None ,
205
+ self ,
206
+ results : list ,
207
+ end : datetime ,
208
+ options : dict ,
209
+ tenant_id : str = None ,
210
+ agreement_type : str = None ,
210
211
) -> list :
211
212
"""Source Data Model"""
212
213
@@ -247,14 +248,14 @@ def _make_transaction_cost_data(self, tenant_id: str, end: datetime) -> list:
247
248
248
249
try :
249
250
for (
250
- reservation_transaction
251
+ reservation_transaction
251
252
) in self .azure_cm_connector .list_reservation_transactions_by_billing_profile_id (
252
253
query_filter
253
254
):
254
255
255
256
if (
256
- reservation_transaction .invoice_section_id .split ("/" )[- 1 ]
257
- == invoice_section_id
257
+ reservation_transaction .invoice_section_id .split ("/" )[- 1 ]
258
+ == invoice_section_id
258
259
):
259
260
reservation_transaction_info = (
260
261
self .azure_cm_connector .convert_nested_dictionary (
@@ -311,12 +312,12 @@ def _make_transaction_cost_data(self, tenant_id: str, end: datetime) -> list:
311
312
return transaction_cost_data
312
313
313
314
def _make_data_info (
314
- self ,
315
- result : dict ,
316
- billed_date : str ,
317
- options : dict ,
318
- tenant_id : str = None ,
319
- agreement_type : str = None ,
315
+ self ,
316
+ result : dict ,
317
+ billed_date : str ,
318
+ options : dict ,
319
+ tenant_id : str = None ,
320
+ agreement_type : str = None ,
320
321
):
321
322
additional_info : dict = self ._get_additional_info (result , options , tenant_id )
322
323
cost : float = self ._get_cost_from_result_with_options (result , options )
@@ -390,8 +391,8 @@ def _get_additional_info(self, result: dict, options: dict, tenant_id: str = Non
390
391
additional_info ["Benefit Name" ] = benefit_name
391
392
392
393
if (
393
- result .get ("pricingmodel" ) == "Reservation"
394
- and result ["metercategory" ] == ""
394
+ result .get ("pricingmodel" ) == "Reservation"
395
+ and result ["metercategory" ] == ""
395
396
):
396
397
result ["metercategory" ] = self ._set_product_from_benefit_name (
397
398
benefit_name
@@ -403,14 +404,14 @@ def _get_additional_info(self, result: dict, options: dict, tenant_id: str = Non
403
404
if result .get ("metersubcategory" ) != "" and result .get ("metersubcategory" ):
404
405
additional_info ["Meter SubCategory" ] = result .get ("metersubcategory" )
405
406
if (
406
- result .get ("pricingmodel" ) == "OnDemand"
407
- and result .get ("metercategory" ) == ""
407
+ result .get ("pricingmodel" ) == "OnDemand"
408
+ and result .get ("metercategory" ) == ""
408
409
):
409
410
result ["metercategory" ] = result .get ("metercategory" )
410
411
411
412
if result .get ("customername" ) is None :
412
413
if result .get ("invoicesectionname" ) != "" and result .get (
413
- "invoicesectionname"
414
+ "invoicesectionname"
414
415
):
415
416
additional_info ["Department Name" ] = result .get ("invoicesectionname" )
416
417
elif result .get ("departmentname" ) != "" and result .get ("departmentname" ):
@@ -419,7 +420,7 @@ def _get_additional_info(self, result: dict, options: dict, tenant_id: str = Non
419
420
if result .get ("accountname" ) != "" and result .get ("accountname" ):
420
421
additional_info ["Enrollment Account Name" ] = result ["accountname" ]
421
422
elif result .get ("enrollmentaccountname" ) != "" and result .get (
422
- "enrollmentaccountname"
423
+ "enrollmentaccountname"
423
424
):
424
425
additional_info ["Enrollment Account Name" ] = result ["enrollmentaccountname" ]
425
426
@@ -428,9 +429,9 @@ def _get_additional_info(self, result: dict, options: dict, tenant_id: str = Non
428
429
429
430
collect_resource_id = options .get ("collect_resource_id" , False )
430
431
if (
431
- collect_resource_id
432
- and result .get ("resourceid" ) != ""
433
- and result .get ("resourceid" )
432
+ collect_resource_id
433
+ and result .get ("resourceid" ) != ""
434
+ and result .get ("resourceid" )
434
435
):
435
436
additional_info ["Resource Id" ] = result ["resourceid" ]
436
437
additional_info ["Resource Name" ] = result ["resourceid" ].split ("/" )[- 1 ]
@@ -488,14 +489,58 @@ def _get_aggregate_data(self, result: dict, options: dict) -> dict:
488
489
489
490
if result .get ("reservationname" ) != "" and result .get ("reservationname" ):
490
491
aggregate_data ["Actual Cost" ] = 0
492
+ elif result .get ("benefitname" ) != "" and result .get ("benefitname" ):
493
+ aggregate_data ["Actual Cost" ] = 0
491
494
else :
492
495
aggregate_data ["Actual Cost" ] = cost_in_billing_currency
493
496
497
+ if result .get ("pricingmodel" ) in ["Reservation" , "SavingsPlan" ]:
498
+ aggregate_data ["Saved Cost" ] = self ._get_saved_cost (
499
+ result , cost_in_billing_currency
500
+ )
501
+
494
502
else :
495
503
aggregate_data ["Actual Cost" ] = cost_in_billing_currency
496
504
497
505
return aggregate_data
498
506
507
+ def _get_saved_cost (self , result : dict , cost : float ) -> float :
508
+ exchange_rate = 1.0
509
+ saved_cost = 0
510
+ currency = result .get ("billingcurrency" , "USD" )
511
+ meter_id = result .get ("meterid" )
512
+ quantity = self ._convert_str_to_float_format (result .get ("quantity" , 0.0 ))
513
+
514
+ if self .retail_price_map .get (meter_id ):
515
+ unit_price = self .retail_price_map [meter_id ]
516
+ else :
517
+ unit_price = self ._get_unit_price_from_meter_id (meter_id )
518
+ self .retail_price_map [meter_id ] = unit_price
519
+
520
+ if currency != "USD" :
521
+ exchange_rate = result .get ("exchangeratepricingtobilling" , 1.0 ) or 1.0
522
+
523
+ retail_cost = exchange_rate * quantity * unit_price
524
+ if retail_cost :
525
+ saved_cost = retail_cost - cost
526
+
527
+ return saved_cost
528
+
529
+ def _get_unit_price_from_meter_id (self , meter_id : str ) -> float :
530
+ unit_price = 0.0
531
+ try :
532
+ response = self .azure_cm_connector .get_retail_price (meter_id )
533
+ items = response .get ("Items" , [])
534
+
535
+ for item in items :
536
+ if item .get ("meterId" ) == meter_id :
537
+ unit_price = item .get ("retailPrice" , 0.0 )
538
+ break
539
+
540
+ except Exception as e :
541
+ _LOGGER .error (f"[_get_unit_price_from_meter_id] get unit price error: { e } " )
542
+ return unit_price
543
+
499
544
@staticmethod
500
545
def _get_region_code (resource_location : str ) -> str :
501
546
return resource_location .lower () if resource_location else resource_location
@@ -524,10 +569,10 @@ def _get_tenant_ids(task_options: dict, collect_scope: str) -> list:
524
569
525
570
@staticmethod
526
571
def _make_scope (
527
- secret_data : dict ,
528
- task_options : dict ,
529
- collect_scope : str ,
530
- customer_tenant_id : str = None ,
572
+ secret_data : dict ,
573
+ task_options : dict ,
574
+ collect_scope : str ,
575
+ customer_tenant_id : str = None ,
531
576
):
532
577
if collect_scope == "subscription_id" :
533
578
subscription_id = task_options ["subscription_id" ]
@@ -628,7 +673,7 @@ def _convert_date_format_to_utc(date_format: str) -> datetime:
628
673
return datetime .strptime (date_format , "%Y-%m-%d" ).replace (tzinfo = timezone .utc )
629
674
630
675
def _make_monthly_time_period (
631
- self , start_date : datetime , end_date : datetime
676
+ self , start_date : datetime , end_date : datetime
632
677
) -> list :
633
678
monthly_time_period = []
634
679
current_date = end_date
@@ -658,7 +703,7 @@ def _make_monthly_time_period(
658
703
659
704
@staticmethod
660
705
def _get_linked_customer_tenants (
661
- secret_data : dict , billing_accounts_info : list
706
+ secret_data : dict , billing_accounts_info : list
662
707
) -> list :
663
708
customer_tenants = secret_data .get ("customer_tenants" , [])
664
709
if not customer_tenants :
@@ -671,7 +716,7 @@ def _get_linked_customer_tenants(
671
716
672
717
@staticmethod
673
718
def _make_accounts_info_from_customer_tenants (
674
- billing_accounts_info : list , customer_tenants : list
719
+ billing_accounts_info : list , customer_tenants : list
675
720
) -> list :
676
721
accounts_info = []
677
722
for billing_account_info in billing_accounts_info :
@@ -709,7 +754,7 @@ def _exclude_cost_data_with_options(result: dict, options: dict) -> bool:
709
754
710
755
@staticmethod
711
756
def _set_network_traffic_cost (
712
- additional_info : dict , product : str , usage_type : str
757
+ additional_info : dict , product : str , usage_type : str
713
758
) -> dict :
714
759
if product in ["Bandwidth" , "Content Delivery Network" ]:
715
760
additional_info ["Usage Type Details" ] = usage_type
0 commit comments