From d399ac5213b4887f890dc67514269f32290c6695 Mon Sep 17 00:00:00 2001 From: ImMin5 Date: Tue, 30 Apr 2024 15:27:24 +0900 Subject: [PATCH] feat: modify change cost data parameter (#199( --- .../cost_analysis/manager/cost_manager.py | 51 ++++++--- .../manager/data_source_account_manager.py | 108 +++++++----------- .../manager/data_source_rule_manager.py | 26 ++--- .../cost_analysis/service/cost_service.py | 3 +- 4 files changed, 85 insertions(+), 103 deletions(-) diff --git a/src/spaceone/cost_analysis/manager/cost_manager.py b/src/spaceone/cost_analysis/manager/cost_manager.py index 51a450e9..712431ed 100644 --- a/src/spaceone/cost_analysis/manager/cost_manager.py +++ b/src/spaceone/cost_analysis/manager/cost_manager.py @@ -50,9 +50,17 @@ def _rollback(vo: Cost): params["billed_year"] = billed_at.strftime("%Y") params["billed_month"] = billed_at.strftime("%Y-%m") - params, ds_account_vo = self.data_source_account_mgr.connect_cost_data(params) + ( + workspace_id, + v_workspace_id, + ) = self.data_source_account_mgr.get_workspace_id_from_account_id( + params["domain_id"], params["data_source_id"] + ) + + if v_workspace_id: + params["workspace_id"] = v_workspace_id - params = self.data_source_rule_mgr.change_cost_data(params, ds_account_vo) + params = self.data_source_rule_mgr.change_cost_data(params, workspace_id) cost_vo: Cost = self.cost_model.create(params) @@ -110,14 +118,14 @@ def get_cost( def filter_costs(self, **conditions): return self.cost_model.filter(**conditions) - def list_costs(self, query: dict, domain_id: str): + def list_costs(self, query: dict, domain_id: str, data_source_id: str): query = self._change_filter_project_group_id(query, domain_id) - # query = self._change_filter_v_workspace_id(query, domain_id) + # query = self._change_filter_v_workspace_id(query, domain_id, data_source_id) return self.cost_model.query(**query) - def stat_costs(self, query: dict, domain_id: str): + def stat_costs(self, query: dict, domain_id: str, data_source_id: str): query = self._change_filter_project_group_id(query, domain_id) - # query = self._change_filter_v_workspace_id(query, domain_id) + # query = self._change_filter_v_workspace_id(query, domain_id, data_source_id) return self.cost_model.stat(**query) @@ -205,14 +213,14 @@ def analyze_costs_by_granularity( granularity = query["granularity"] # Change filter v_workspace_id to workspace_id - data_source_vo = self.data_source_mgr.get_data_source(data_source_id, domain_id) - plugin_metadata = data_source_vo.plugin_info.metadata - use_account_routing = plugin_metadata.get("use_account_routing", False) - if use_account_routing: - _LOGGER.debug( - f"[analyze_costs_by_granularity] add v_workspace_id filter: {data_source_id}" - ) - query = self._change_filter_v_workspace_id(query, domain_id) + # data_source_vo = self.data_source_mgr.get_data_source(data_source_id, domain_id) + # plugin_metadata = data_source_vo.plugin_info.metadata + # use_account_routing = plugin_metadata.get("use_account_routing", False) + # if use_account_routing: + # _LOGGER.debug( + # f"[analyze_costs_by_granularity] add v_workspace_id filter: {data_source_id}" + # ) + query = self._change_filter_v_workspace_id(query, domain_id, data_source_id) # Save query history to speed up data loading query_hash: str = utils.dict_to_hash(query) @@ -446,10 +454,21 @@ def _change_filter_project_group_id(self, query: dict, domain_id: str) -> dict: query["filter"] = change_filter return query - def _change_filter_v_workspace_id(self, query: dict, domain_id: str) -> dict: + def _change_filter_v_workspace_id( + self, query: dict, domain_id: str, data_source_id: str + ) -> dict: change_filter = [] workspace_ids = [] - data_source_id = self._get_data_source_id_from_filter(query) + + data_source_vo = self.data_source_mgr.get_data_source(data_source_id, domain_id) + plugin_metadata = data_source_vo.plugin_info.metadata + use_account_routing = plugin_metadata.get("use_account_routing", False) + if not use_account_routing: + return query + + _LOGGER.debug( + f"[analyze_costs_by_granularity] add v_workspace_id filter: {data_source_id}" + ) for condition in query.get("filter", []): key = condition.get("k", condition.get("key")) diff --git a/src/spaceone/cost_analysis/manager/data_source_account_manager.py b/src/spaceone/cost_analysis/manager/data_source_account_manager.py index 216dfdbd..04482e24 100644 --- a/src/spaceone/cost_analysis/manager/data_source_account_manager.py +++ b/src/spaceone/cost_analysis/manager/data_source_account_manager.py @@ -3,7 +3,6 @@ from mongoengine import QuerySet from spaceone.core.manager import BaseManager -from spaceone.core import utils from spaceone.cost_analysis.model import DataSource from spaceone.cost_analysis.model.data_source_account.database import DataSourceAccount @@ -83,45 +82,28 @@ def filter_data_source_accounts(self, **conditions) -> QuerySet: def stat_data_source_accounts(self, query: dict) -> dict: return self.data_source_account_model.stat(**query) - def connect_cost_data( - self, cost_data: dict - ) -> Tuple[dict, Union[DataSourceAccount, None]]: - data_source_id = cost_data["data_source_id"] - domain_id = cost_data["domain_id"] + def get_workspace_id_from_account_id( + self, domain_id: str, data_source_id: str + ) -> Tuple[str, str]: + workspace_id = None + v_workspace_id = None data_source_vo = self._get_data_source(data_source_id, domain_id) plugin_info_metadata = data_source_vo.plugin_info.metadata use_account_routing = plugin_info_metadata.get("use_account_routing", False) - ds_account_vo = None if use_account_routing: - account_connect_polices: list = plugin_info_metadata.get( - "account_connect_polices" - ) - for account_connect_policy in account_connect_polices: - if account_connect_policy.get("name") == "connect_cost_to_account": - name = account_connect_policy.get("name") - policy = account_connect_policy["polices"].get(name) - source = policy["source"] - target_key = policy.get("target", "account_id") - target_value = utils.get_dict_value(cost_data, source) - operator = policy.get("operator") - - if target_value: - ds_account_vo = self._get_data_source_account_vo( - target_key, - target_value, - data_source_id, - domain_id, - operator, - ) - - if ds_account_vo: - cost_data["account_id"] = ds_account_vo.account_id - cost_data["workspace_id"] = ds_account_vo.v_workspace_id - - return cost_data, ds_account_vo + account_match_key_value = plugin_info_metadata.get("account_match_key") + if account_match_key_value: + ds_account_vo = self.get_data_source_account( + data_source_id, account_match_key_value, domain_id + ) + + workspace_id = ds_account_vo.workspace_id + v_workspace_id = ds_account_vo.v_workspace_id + + return workspace_id, v_workspace_id def connect_account_by_data_source_vo( self, @@ -130,52 +112,30 @@ def connect_account_by_data_source_vo( ) -> DataSourceAccount: domain_id = data_source_vo.domain_id - plugin_info_metadata = data_source_vo.plugin_info.metadata - account_connect_polices: list = plugin_info_metadata.get( - "account_connect_polices" - ) - - for account_connect_policy in account_connect_polices: - if account_connect_policy.get("name") == "connect_account_to_workspace": - name = account_connect_policy.get("name") - policy = account_connect_policy["polices"].get(name) - source = policy["source"] - target_key = policy.get("target", "references") + reference_id = data_source_account_vo.account_id + workspace_info = self._get_workspace_by_references(reference_id, domain_id) + if workspace_info: + data_source_account_vo = self.update_data_source_account_by_vo( + {"workspace_id": workspace_info.get("workspace_id")}, + data_source_account_vo, + ) - target_value = utils.get_dict_value( - data_source_account_vo.to_dict(), - source, - ) - operator = policy.get("operator") - - if target_value: - workspace_info = self._get_workspace( - target_key, target_value, domain_id, operator - ) - - if workspace_info: - data_source_account_vo = self.update_data_source_account_by_vo( - {"workspace_id": workspace_info.get("workspace_id")}, - data_source_account_vo, - ) return data_source_account_vo - def _get_workspace( - self, target_key: str, target_value: str, domain_id: str, operator: str = "eq" + def _get_workspace_by_references( + self, reference_id: str, domain_id: str ) -> Union[dict, None]: - if f"workspace:{domain_id}:{target_key}:{target_value}" in self._workspace_info: + if f"workspace:{domain_id}:references:{reference_id}" in self._workspace_info: return self._workspace_info[ - f"workspace:{domain_id}:{target_key}:{target_value}" + f"workspace:{domain_id}:references:{reference_id}" ] query = { "filter": [ {"k": "domain_id", "v": domain_id, "o": "eq"}, + {"k": "references", "v": [reference_id], "o": "in"}, ] } - if operator == "in" and not isinstance(target_value, list): - target_value = [target_value] - query["filter"].append({"k": target_key, "v": target_value, "o": operator}) identity_mgr = self.locator.get_manager("IdentityManager") response = identity_mgr.list_workspaces({"query": query}, domain_id) @@ -183,11 +143,21 @@ def _get_workspace( total_count = response.get("total_count", 0) workspace_info = None - if total_count > 0: + + if total_count == 0: + response = identity_mgr.list_workspaces( + {"query": {"filter": [{"k": "domain_id", "v": domain_id, "o": "eq"}]}}, + domain_id, + ) + total_count = response.get("total_count", 0) + if total_count == 1: + workspace_info = response.get("results")[0] + + else: workspace_info = results[0] self._workspace_info[ - f"workspace:{domain_id}:{target_key}:{target_value}" + f"workspace:{domain_id}:references:{reference_id}" ] = workspace_info return workspace_info diff --git a/src/spaceone/cost_analysis/manager/data_source_rule_manager.py b/src/spaceone/cost_analysis/manager/data_source_rule_manager.py index bf727cc3..98161959 100644 --- a/src/spaceone/cost_analysis/manager/data_source_rule_manager.py +++ b/src/spaceone/cost_analysis/manager/data_source_rule_manager.py @@ -5,7 +5,6 @@ from spaceone.core import utils from spaceone.core.manager import BaseManager from spaceone.cost_analysis.manager.identity_manager import IdentityManager -from spaceone.cost_analysis.model import DataSourceAccount from spaceone.cost_analysis.model.data_source_rule_model import ( DataSourceRule, DataSourceRuleCondition, @@ -88,9 +87,7 @@ def list_data_source_rules(self, query: dict): def stat_data_source_rules(self, query): return self.data_source_rule_model.stat(**query) - def change_cost_data( - self, cost_data: dict, data_source_account_vo: DataSourceAccount = None - ) -> dict: + def change_cost_data(self, cost_data: dict, workspace_id: str = None) -> dict: data_source_id = cost_data["data_source_id"] domain_id = cost_data["domain_id"] ( @@ -99,11 +96,11 @@ def change_cost_data( ) = self._get_data_source_rules(data_source_id, domain_id) cost_data = self._apply_data_source_rule_to_cost_data( - cost_data, managed_data_source_rule_vos, domain_id, data_source_account_vo + cost_data, managed_data_source_rule_vos, domain_id, workspace_id ) cost_data = self._apply_data_source_rule_to_cost_data( - cost_data, custom_data_source_rule_vos, domain_id, data_source_account_vo + cost_data, custom_data_source_rule_vos, domain_id, workspace_id ) return cost_data @@ -113,7 +110,7 @@ def _apply_data_source_rule_to_cost_data( cost_data: dict, data_source_rule_vos: QuerySet, domain_id: str, - data_source_account_vo: DataSourceAccount = None, + workspace_id: str = None, ): for data_source_rule_vo in data_source_rule_vos: is_match = self._change_cost_data_by_rule(cost_data, data_source_rule_vo) @@ -122,7 +119,7 @@ def _apply_data_source_rule_to_cost_data( cost_data, data_source_rule_vo.actions, domain_id, - data_source_account_vo, + workspace_id, ) if is_match and data_source_rule_vo.options.stop_processing: @@ -135,13 +132,8 @@ def _change_cost_data_with_actions( cost_data: dict, actions: dict, domain_id: str, - data_source_account_vo: DataSourceAccount = None, + workspace_id: str = None, ): - if data_source_account_vo: - workspace_id = data_source_account_vo.workspace_id - else: - workspace_id = None - for action, value in actions.items(): if action == "change_project" and value: cost_data["project_id"] = value @@ -156,8 +148,8 @@ def _change_cost_data_with_actions( ) if project_info: cost_data["project_id"] = project_info["project_id"] - if not data_source_account_vo: - cost_data["workspace_id"] = project_info.get("workspace_id") + if not workspace_id: + cost_data["workspace_id"] = project_info["workspace_id"] elif action == "match_service_account" and value: source = value["source"] @@ -172,7 +164,7 @@ def _change_cost_data_with_actions( "service_account_id" ] cost_data["project_id"] = service_account_info.get("project_id") - if not data_source_account_vo: + if not workspace_id: cost_data["workspace_id"] = service_account_info.get( "workspace_id" ) diff --git a/src/spaceone/cost_analysis/service/cost_service.py b/src/spaceone/cost_analysis/service/cost_service.py index 191b328c..879bcb5c 100644 --- a/src/spaceone/cost_analysis/service/cost_service.py +++ b/src/spaceone/cost_analysis/service/cost_service.py @@ -173,7 +173,8 @@ def list(self, params): query = params.get("query", {}) domain_id = params["domain_id"] - return self.cost_mgr.list_costs(query, domain_id) + data_source_id = params["data_source_id"] + return self.cost_mgr.list_costs(query, domain_id, data_source_id) @transaction( permission="cost-analysis:Cost.read",