diff --git a/Dockerfile b/Dockerfile index 7fb6e12..3499b99 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM cloudforet/python-core:2.0 +FROM cloudforet/python-core:2.0.90 ENV PYTHONUNBUFFERED 1 ENV SPACEONE_PORT 50051 diff --git a/src/cloudforet/cost_analysis/connector/azure_cost_mgmt_connector.py b/src/cloudforet/cost_analysis/connector/azure_cost_mgmt_connector.py index 0b45a7f..eef8425 100644 --- a/src/cloudforet/cost_analysis/connector/azure_cost_mgmt_connector.py +++ b/src/cloudforet/cost_analysis/connector/azure_cost_mgmt_connector.py @@ -206,7 +206,7 @@ def query_usage_http( yield response_json except Exception as e: _LOGGER.error(f"[ERROR] query_usage_http {e}", exc_info=True) - raise ERROR_UNKNOWN(message=f"[ERROR] get_usd_cost_and_tag_http {e}") + raise ERROR_UNKNOWN(message=f"[ERROR] query_usage_http {e}") def get_billing_account(self) -> dict: billing_account_name = self.billing_account_id diff --git a/src/cloudforet/cost_analysis/manager/cost_manager.py b/src/cloudforet/cost_analysis/manager/cost_manager.py index 9e624e7..cc674c4 100644 --- a/src/cloudforet/cost_analysis/manager/cost_manager.py +++ b/src/cloudforet/cost_analysis/manager/cost_manager.py @@ -157,7 +157,7 @@ def get_data( collect_scope: str = task_options["collect_scope"] tenant_ids: list = self._get_tenant_ids(task_options, collect_scope) start: datetime = self._get_first_date_of_month(task_options["start"]) - end: datetime = datetime.utcnow() + end = self._get_end_date_from_task_options(task_options) monthly_time_period = self._make_monthly_time_period(start, end) for time_period in monthly_time_period: @@ -454,6 +454,20 @@ def _get_additional_info(self, result: dict, options: dict, tenant_id: str = Non if result.get("term") != "" and result.get("term"): additional_info["Term"] = result["term"] + if options.get("cost_metric") == "AmortizedCost": + if result.get("pricingmodel") in ["Reservation", "SavingsPlan"]: + meter_id = result.get("meterid") + product_id = result.get("productid") + if not self.retail_price_map.get(f"{meter_id}:{product_id}"): + unit_price = self._get_unit_price_from_meter_id( + meter_id, product_id + ) + self.retail_price_map[f"{meter_id}:{product_id}"] = unit_price + + additional_info["PayG Unit Price"] = self.retail_price_map[ + f"{meter_id}:{product_id}" + ] + return additional_info def _get_cost_from_result_with_options(self, result: dict, options: dict) -> float: @@ -498,9 +512,12 @@ def _get_aggregate_data(self, result: dict, options: dict) -> dict: 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 - ) + if cost_in_billing_currency > 0: + aggregate_data["Saved Cost"] = self._get_saved_cost( + result, cost_in_billing_currency + ) + else: + aggregate_data["Saved Cost"] = 0.0 else: aggregate_data["Actual Cost"] = cost_in_billing_currency @@ -578,6 +595,8 @@ def _get_tenant_ids(task_options: dict, collect_scope: str) -> list: tenant_ids.append(task_options["tenant_id"]) elif collect_scope == "customer_tenant_id": tenant_ids.extend(task_options["customer_tenants"]) + elif "billing_tenant_id" in task_options: + tenant_ids.append(task_options["billing_tenant_id"]) else: tenant_ids.append("EA Agreement") return tenant_ids @@ -780,3 +799,11 @@ def _set_network_traffic_cost( def _combine_rows_and_columns_from_results(rows: list, columns: list): _columns = [column.get("name") for column in columns] return pd.DataFrame(data=rows, columns=_columns).to_dict(orient="records") + + def _get_end_date_from_task_options(self, task_options: dict) -> datetime: + end_date = datetime.utcnow() + + if "end" in task_options: + if end_date.strftime("%Y-%m") < task_options["end"]: + end_date = self._get_last_date_of_month(end_date.year, end_date.month) + return end_date diff --git a/src/cloudforet/cost_analysis/manager/job_manager.py b/src/cloudforet/cost_analysis/manager/job_manager.py index 7873147..5d766ff 100644 --- a/src/cloudforet/cost_analysis/manager/job_manager.py +++ b/src/cloudforet/cost_analysis/manager/job_manager.py @@ -43,61 +43,96 @@ def get_tasks( changed = [] synced_accounts = [] - # divide customer tenants for each task - customer_tenants, first_sync_tenants = self._get_customer_tenants( - secret_data, linked_accounts - ) - - if len(customer_tenants) == 0 and len(first_sync_tenants) > 0: - customer_tenants.extend(first_sync_tenants) - first_sync_tenants = [] + # Only for MicrosoftPartnerAgreement + if options.get("collect_scope") == "billing_account_id": + start = datetime.strptime(start_month, "%Y-%m") + end = datetime.utcnow() - divided_customer_tenants = self._get_divided_customer_tenants( - customer_tenants - ) + month_range = relativedelta(end, start).months + month_range_step = math.ceil((month_range + 1) / _TASK_LIST_SIZE) - for divided_customer_tenant_info in divided_customer_tenants: - tasks.append( - { - "task_options": { - "start": start_month, - "account_agreement_type": billing_account_agreement_type, - "collect_scope": "customer_tenant_id", - "customer_tenants": divided_customer_tenant_info, - "billing_tenant_id": secret_data["tenant_id"], - } - } - ) - if linked_accounts: - synced_accounts = self._extend_synced_accounts( - synced_accounts, divided_customer_tenant_info + for month in range( + 0, + month_range + 1, + math.ceil((month_range + 1) / _TASK_LIST_SIZE), + ): + task_start_month = datetime.strftime( + start + relativedelta(months=month), "%Y-%m" + ) + task_end_month = datetime.strftime( + start + relativedelta(months=month + month_range_step - 1), + "%Y-%m", ) - changed.append({"start": start_month}) - if first_sync_tenants: - first_sync_start_month = self._get_start_month(start=None) - tasks.append( - { - "task_options": { - "start": first_sync_start_month, - "account_agreement_type": billing_account_agreement_type, - "collect_scope": "customer_tenant_id", - "customer_tenants": first_sync_tenants, - "billing_tenant_id": secret_data["tenant_id"], - "is_sync": False, + tasks.append( + { + "task_options": { + "start": task_start_month, + "end": task_end_month, + "account_agreement_type": billing_account_agreement_type, + "collect_scope": "billing_account_id", + "billing_tenant_id": secret_data["tenant_id"], + } } - } + ) + if linked_accounts: + synced_accounts = linked_accounts + changed.append({"start": start_month}) + else: + # divide customer tenants for each task + customer_tenants, first_sync_tenants = self._get_customer_tenants( + secret_data, linked_accounts + ) + + if len(customer_tenants) == 0 and len(first_sync_tenants) > 0: + customer_tenants.extend(first_sync_tenants) + first_sync_tenants = [] + + divided_customer_tenants = self._get_divided_customer_tenants( + customer_tenants ) - for tenant_id in first_sync_tenants: - changed.append( + + for divided_customer_tenant_info in divided_customer_tenants: + tasks.append( { - "start": first_sync_start_month, - "filter": {"additional_info.Tenant Id": tenant_id}, + "task_options": { + "start": start_month, + "account_agreement_type": billing_account_agreement_type, + "collect_scope": "customer_tenant_id", + "customer_tenants": divided_customer_tenant_info, + "billing_tenant_id": secret_data["tenant_id"], + } } ) - if linked_accounts: - synced_accounts = self._extend_synced_accounts( - synced_accounts, first_sync_tenants + if linked_accounts: + synced_accounts = self._extend_synced_accounts( + synced_accounts, divided_customer_tenant_info + ) + changed.append({"start": start_month}) + if first_sync_tenants: + first_sync_start_month = self._get_start_month(start=None) + tasks.append( + { + "task_options": { + "start": first_sync_start_month, + "account_agreement_type": billing_account_agreement_type, + "collect_scope": "customer_tenant_id", + "customer_tenants": first_sync_tenants, + "billing_tenant_id": secret_data["tenant_id"], + "is_sync": False, + } + } ) + for tenant_id in first_sync_tenants: + changed.append( + { + "start": first_sync_start_month, + "filter": {"additional_info.Tenant Id": tenant_id}, + } + ) + if linked_accounts: + synced_accounts = self._extend_synced_accounts( + synced_accounts, first_sync_tenants + ) else: tasks = [ {