diff --git a/src/spaceone/identity/conf/global_conf.py b/src/spaceone/identity/conf/global_conf.py index a163ecc1..cba27e3c 100644 --- a/src/spaceone/identity/conf/global_conf.py +++ b/src/spaceone/identity/conf/global_conf.py @@ -17,7 +17,7 @@ ] # Dormancy Settings -DORMANCY_CHECK_HOUR = 13 +DORMANCY_CHECK_HOUR = 14 DORMANCY_SETTINGS_KEY = "identity:dormancy:workspace" # Database Settings diff --git a/src/spaceone/identity/manager/cost_analysis_manager.py b/src/spaceone/identity/manager/cost_analysis_manager.py index 7430ee02..819d634a 100644 --- a/src/spaceone/identity/manager/cost_analysis_manager.py +++ b/src/spaceone/identity/manager/cost_analysis_manager.py @@ -14,21 +14,11 @@ def __init__(self, *args, **kwargs): "SpaceConnector", service="cost_analysis" ) - def analyze_cost_report_data( + def list_cost_reports( self, params: dict, token: str = None, x_domain_id: str = None ) -> dict: return self.cost_analysis_conn.dispatch( - "CostReportData.analyze", - params, - token=token, - x_domain_id=x_domain_id, - ) - - def list_cost_report_configs( - self, params: dict, token: str = None, x_domain_id: str = None - ) -> dict: - return self.cost_analysis_conn.dispatch( - "CostReportConfig.list", + "CostReport.list", params, token=token, x_domain_id=x_domain_id, diff --git a/src/spaceone/identity/manager/service_account_manager.py b/src/spaceone/identity/manager/service_account_manager.py index 7a07b35b..6846141d 100644 --- a/src/spaceone/identity/manager/service_account_manager.py +++ b/src/spaceone/identity/manager/service_account_manager.py @@ -23,6 +23,10 @@ def _rollback(vo: ServiceAccount): service_account_vo.delete() params["state"] = "ACTIVE" + params["cost_info"] = { + "day": 0, + "month": 0, + } service_account_vo = self.service_account_model.create(params) self.transaction.add_rollback(_rollback, service_account_vo) diff --git a/src/spaceone/identity/manager/workspace_manager.py b/src/spaceone/identity/manager/workspace_manager.py index 92480f45..3a5147f9 100644 --- a/src/spaceone/identity/manager/workspace_manager.py +++ b/src/spaceone/identity/manager/workspace_manager.py @@ -23,13 +23,20 @@ def _rollback(vo: Workspace): ) vo.delete() + params["dormant_ttl"] = -1 + params["service_account_count"] = 0 + params["cost_info"] = { + "day": 0, + "month": 0, + } + workspace_vo = self.workspace_model.create(params) self.transaction.add_rollback(_rollback, workspace_vo) return workspace_vo def update_workspace_by_vo( - self, params: dict, workspace_vo: Workspace + self, params: dict, workspace_vo: Workspace ) -> Workspace: def _rollback(old_data): _LOGGER.info( @@ -50,7 +57,8 @@ def delete_workspace_by_vo(workspace_vo: Workspace) -> None: if rb_vos.count() > 0: _LOGGER.debug( - f"[delete_workspace_by_vo] Delete role bindings count with {workspace_vo.workspace_id} : {rb_vos.count()}") + f"[delete_workspace_by_vo] Delete role bindings count with {workspace_vo.workspace_id} : {rb_vos.count()}" + ) rb_vos.delete() workspace_vo.delete() diff --git a/src/spaceone/identity/model/service_account/database.py b/src/spaceone/identity/model/service_account/database.py index 0e1f5fa9..0b41f1fd 100644 --- a/src/spaceone/identity/model/service_account/database.py +++ b/src/spaceone/identity/model/service_account/database.py @@ -17,7 +17,6 @@ class ServiceAccount(MongoModel): tags = DictField(default=None) reference_id = StringField(max_length=255, default=None, null=True) is_managed = BooleanField(default=False) - asset_info = DictField(default=None) cost_info = DictField(default=None) secret_schema_id = StringField(max_length=40) secret_id = StringField(max_length=40) @@ -37,13 +36,13 @@ class ServiceAccount(MongoModel): "data", "tags", "is_managed", - "asset_info", "cost_info", "secret_schema_id", "secret_id", "trusted_account_id", "project_id", "last_synced_at", + "inactivated_at", ], "minimal_fields": [ "service_account_id", diff --git a/src/spaceone/identity/model/workspace/database.py b/src/spaceone/identity/model/workspace/database.py index b730cdfe..ca288671 100644 --- a/src/spaceone/identity/model/workspace/database.py +++ b/src/spaceone/identity/model/workspace/database.py @@ -17,7 +17,7 @@ class Workspace(MongoModel): is_managed = BooleanField(default=False) is_dormant = BooleanField(default=False) - dormant_ttl = IntField(default=None) + dormant_ttl = IntField(default=None, required=True) service_account_count = IntField(default=None) cost_info = DictField(default=None) @@ -42,6 +42,7 @@ class Workspace(MongoModel): "references", "deleted_at", "last_synced_at", + "dormant_updated_at", ], "minimal_fields": [ "workspace_id", diff --git a/src/spaceone/identity/service/job_service.py b/src/spaceone/identity/service/job_service.py index 8d8ee8df..f5542057 100644 --- a/src/spaceone/identity/service/job_service.py +++ b/src/spaceone/identity/service/job_service.py @@ -200,16 +200,9 @@ def check_dormancy(self, params: dict) -> None: domain_mgr = DomainManager() - # Temporary Code - # from spaceone.core import config, pygrpc, fastapi, utils, model - # - # model.init_all(False) - - # domain_vos = domain_mgr.filter_domains( - # state="ENABLED", domain_id="domain-286776a1516a" - # ) - # for domain_vo in domain_vos: - # self.job_mgr.push_dormancy_job({"domain_id": domain_vo.domain_id}) + domain_vos = domain_mgr.filter_domains(state="ENABLED") + for domain_vo in domain_vos: + self.job_mgr.push_dormancy_job({"domain_id": domain_vo.domain_id}) @transaction(exclude=["authentication", "authorization", "mutation"]) def check_dormancy_by_domain(self, params: dict) -> None: @@ -225,15 +218,20 @@ def check_dormancy_by_domain(self, params: dict) -> None: domain_id = params["domain_id"] dormancy_settings = self._get_dormancy_settings(domain_id) dormancy_state = dormancy_settings.get("state") + threshold = dormancy_settings.get("cost") + dormancy_send_email = dormancy_settings.get("send_email") cost_analysis_mgr = CostAnalysisManager() - is_report_exists, currency = self._get_currency(cost_analysis_mgr, domain_id) workspace_vos = self.workspace_mgr.filter_workspaces(domain_id=domain_id) for workspace_vo in workspace_vos: + if self._is_dormancy_updated(workspace_vo): + continue + cost_info = workspace_vo.cost_info or {} before_month_cost = cost_info.get("month", 0) - before_day_cost = cost_info.get("day", 0) + before_dormant_ttl = workspace_vo.dormant_ttl or -1 + before_is_dormant = workspace_vo.is_dormant update_params = { "is_dormant": workspace_vo.is_dormant, @@ -248,39 +246,66 @@ def check_dormancy_by_domain(self, params: dict) -> None: update_params["service_account_count"] = service_account_vos.count() - if is_report_exists: - month_cost = self._get_this_month_cost( - cost_analysis_mgr, domain_id, workspace_id, currency - ) - update_params["cost_info"] = {"month": month_cost, "day": 0} + month_cost = self._get_this_month_cost( + cost_analysis_mgr, domain_id, workspace_id + ) + + day_cost = month_cost - before_month_cost + if day_cost < 0: + day_cost = 0 + + update_params["cost_info"] = {"month": month_cost, "day": day_cost} + + if dormancy_state == "ENABLED" and before_dormant_ttl >= 0: + if day_cost <= threshold: + _LOGGER.debug( + f"[check_dormancy_by_domain] change dormant: {workspace_vo.name}" + ) + update_params["is_dormant"] = True + + if before_dormant_ttl > 0: + dormant_ttl = before_dormant_ttl - 1 + if dormancy_send_email: + # Send Email + if dormant_ttl == 0: + pass + else: + pass + + update_params["dormant_ttl"] = dormant_ttl + else: + if before_is_dormant: + _LOGGER.debug( + f"[check_dormancy_by_domain] change active: {workspace_vo.name}" + ) - if dormancy_state == "ENABLED": - # Check Dormancy and Notify - pass + update_params["is_dormant"] = False + update_params["dormant_ttl"] = 3 + + update_params["dormant_updated_at"] = datetime.utcnow() + self.workspace_mgr.update_workspace_by_vo(update_params, workspace_vo) @staticmethod - def _get_currency( - cost_analysis_mgr: CostAnalysisManager, domain_id: str - ) -> Tuple[bool, str]: - system_token = config.get_global("TOKEN") + def _is_dormancy_updated(workspace_vo: Workspace) -> bool: + if not workspace_vo.dormant_updated_at: + return False - # Get Cost Report Config - response = cost_analysis_mgr.list_cost_report_configs( - {}, token=system_token, x_domain_id=domain_id - ) - if response.get("total_count", 0) == 0: - return False, "" + today = datetime.utcnow().strftime("%Y-%m-%d") + last_dormant_updated_date = workspace_vo.dormant_updated_at.strftime("%Y-%m-%d") - cost_report_config = response["results"][0] - currency = cost_report_config.get("currency", "USD") - return True, currency + if today == last_dormant_updated_date: + _LOGGER.debug( + f"[_is_dormancy_updated] dormant has already been updated: {workspace_vo.name}" + ) + return True + else: + return False @staticmethod def _get_this_month_cost( cost_analysis_mgr: CostAnalysisManager, domain_id: str, workspace_id: str, - currency: str, ) -> float: system_token = config.get_global("TOKEN") now = datetime.utcnow() @@ -288,22 +313,22 @@ def _get_this_month_cost( # Get Monthly Cost params = { + "status": "IN_PROGRESS", "query": { - "granularity": "MONTHLY", - "start": report_month, - "end": report_month, - "fields": {"cost": {"key": f"cost.{currency}", "operator": "sum"}}, - "filter": [{"k": "is_confirmed", "v": False, "o": "eq"}], + "filter": [ + {"k": "report_month", "v": report_month, "o": "eq"}, + {"k": "workspace_id", "v": workspace_id, "o": "eq"}, + ] }, - "workspace_id": workspace_id, } - response = cost_analysis_mgr.analyze_cost_report_data( + response = cost_analysis_mgr.list_cost_reports( params, token=system_token, x_domain_id=domain_id ) results = response.get("results", []) if len(results) > 0: - return results[0]["cost"] + currency = results[0]["currency"] + return results[0]["cost"][currency] else: return 0