From 5f4007846ab795c2d39be3a95ac3ec53566fdbac Mon Sep 17 00:00:00 2001 From: durant <826035498@qq.com> Date: Mon, 18 Nov 2024 17:22:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(backend):=20=E6=94=AF=E6=8C=81=E5=91=8A?= =?UTF-8?q?=E8=AD=A6=E5=B1=8F=E8=94=BD=20#8017?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat(backend): 支持告警屏蔽 #8017 --- dbm-ui/backend/bk_web/viewsets.py | 5 +- .../backend/components/bkmonitorv3/client.py | 25 ++++ dbm-ui/backend/db_monitor/mock_data.py | 131 ++++++++++++++++++ dbm-ui/backend/db_monitor/serializers.py | 53 +++++++ dbm-ui/backend/db_monitor/urls.py | 2 + dbm-ui/backend/db_monitor/utils.py | 7 + dbm-ui/backend/db_monitor/views/shield.py | 98 +++++++++++++ 7 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 dbm-ui/backend/db_monitor/views/shield.py diff --git a/dbm-ui/backend/bk_web/viewsets.py b/dbm-ui/backend/bk_web/viewsets.py index d3c68495bf..3271859091 100644 --- a/dbm-ui/backend/bk_web/viewsets.py +++ b/dbm-ui/backend/bk_web/viewsets.py @@ -74,7 +74,10 @@ def validated_data(self): data.setdefault("bk_username", bk_username) data.setdefault("bk_app_code", bk_app_code) - serializer = self.serializer_class or self.get_serializer_class() + if self.serializer_class and self.serializer_class is not serializers.Serializer: + serializer = self.serializer_class + else: + serializer = self.get_serializer_class() return self.params_validate(serializer, data) def params_validate(self, slz_cls, context: Optional[Dict] = None, init_params: Optional[Dict] = None, **kwargs): diff --git a/dbm-ui/backend/components/bkmonitorv3/client.py b/dbm-ui/backend/components/bkmonitorv3/client.py index 78b3dd96b0..4e916b6450 100644 --- a/dbm-ui/backend/components/bkmonitorv3/client.py +++ b/dbm-ui/backend/components/bkmonitorv3/client.py @@ -190,6 +190,31 @@ def __init__(self): url="edit_action_config/", description=_("编辑处理套餐"), ) + self.add_shield = self.generate_data_api( + method="POST", + url="add_shield/", + description=_("新增告警屏蔽"), + ) + self.disable_shield = self.generate_data_api( + method="POST", + url="disable_shield/", + description=_("解除告警屏蔽"), + ) + self.edit_shield = self.generate_data_api( + method="POST", + url="edit_shield/", + description=_("编辑告警屏蔽"), + ) + self.list_shield = self.generate_data_api( + method="POST", + url="list_shield/", + description=_("获取告警屏蔽列表"), + ) + self.get_shield = self.generate_data_api( + method="GET", + url="get_shield/", + description=_("获取告警屏蔽详情"), + ) BKMonitorV3Api = _BKMonitorV3Api() diff --git a/dbm-ui/backend/db_monitor/mock_data.py b/dbm-ui/backend/db_monitor/mock_data.py index b85bb938d3..4a7c3eef9d 100644 --- a/dbm-ui/backend/db_monitor/mock_data.py +++ b/dbm-ui/backend/db_monitor/mock_data.py @@ -722,7 +722,138 @@ "user_groups": [20], }, } + CREATE_POLICY = [ {"name": "influxDB-n6hfH", "target_priority": 0, "details": CREATE_POLICY_DETAILS}, {"name": "MySql123", "target_priority": 1, "details": CREATE_POLICY_DETAILS}, ] + +# 范围屏蔽(主机) +CREATE_ALARM_SHIELD_FOR_IP_SCOPE = { + "category": "scope", + "begin_time": "2024-11-21 00:00:00", + "end_time": "2024-11-21 23:59:59", + "cycle_config": {"begin_time": "", "end_time": "", "day_list": [], "week_list": [], "type": 1}, + "shield_notice": False, + "notice_config": {}, + "description": "xxxx", + "dimension_config": {"scope_type": "ip", "target": [{"bk_host_id": 1, "ip": "127.0.0.1", "bk_cloud_id": 0}]}, + "bk_biz_id": 3, +} + +# 维度屏蔽 +CREATE_ALARM_SHIELD_FOR_DIMENSION = { + "category": "dimension", + "begin_time": "2024-11-21 00:00:00", + "end_time": "2024-11-21 23:59:59", + "cycle_config": {"begin_time": "", "end_time": "", "day_list": [], "week_list": [], "type": 1}, + "shield_notice": False, + "notice_config": {}, + "description": "test", + "dimension_config": { + "dimension_conditions": [ + {"condition": "and", "key": "appid", "method": "eq", "value": ["100706"], "name": "appid"} + ], + "strategy_id": "", + }, + "bk_biz_id": 3, +} + +# 策略屏蔽 +CREATE_ALARM_SHIELD_FOR_STRATEGY = { + "category": "strategy", + "begin_time": "2024-11-21 00:00:00", + "end_time": "2024-11-21 23:59:59", + "cycle_config": {"begin_time": "", "end_time": "", "day_list": [], "week_list": [], "type": 1}, + "shield_notice": False, + "notice_config": {}, + "description": "xxxx", + "dimension_config": { + "id": [98043], + "level": [2], + "dimension_conditions": [ + {"condition": "and", "key": "appid", "method": "eq", "value": ["11111111"], "name": "dbm_meta app id"} + ], + }, + "level": [2], + "bk_biz_id": 3, +} + + +# 更新策略屏蔽 +UPDATE_ALARM_SHIELD = {"begin_time": "2024-11-21 00:00:00", "end_time": "2024-12-21 23:59:59", "description": "update"} + +LIST_ALARM_SHIELD = {"bk_biz_id": 3} + +LIST_ALARM_SHIELD_RESPONSE = [ + { + "id": 769279, + "bk_biz_id": 5005578, + "category": "strategy", + "status": 1, + "begin_time": "2024-11-21 00:00:00", + "end_time": "2024-11-21 23:59:59", + "failure_time": "2024-11-21 23:59:59", + "is_enabled": True, + "scope_type": "", + "dimension_config": { + "strategy_id": [98043], + "level": [2], + "dimension_conditions": [ + {"key": "appid", "value": ["11111111"], "method": "eq", "condition": "and", "name": "dbm_meta app id"} + ], + }, + "content": "", + "cycle_config": {"type": 1, "week_list": [], "day_list": [], "begin_time": "", "end_time": ""}, + "shield_notice": False, + "notice_config": "{}", + "description": "xxxx", + "source": "", + "update_user": "admin", + }, + { + "id": 769271, + "bk_biz_id": 5005578, + "category": "dimension", + "status": 1, + "begin_time": "2024-11-21 00:00:00", + "end_time": "2024-11-21 23:59:59", + "failure_time": "2024-11-21 23:59:59", + "is_enabled": True, + "scope_type": "", + "dimension_config": { + "dimension_conditions": [ + {"key": "appid", "value": ["100706"], "method": "eq", "condition": "and", "name": "appid"} + ], + "_strategy_id": 0, + }, + "content": "", + "cycle_config": {"type": 1, "week_list": [], "day_list": [], "begin_time": "", "end_time": ""}, + "shield_notice": False, + "notice_config": "{}", + "description": "test", + "source": "", + "update_user": "admin", + }, + { + "id": 769270, + "bk_biz_id": 5005578, + "category": "scope", + "status": 1, + "begin_time": "2024-11-21 00:00:00", + "end_time": "2024-11-21 23:59:59", + "failure_time": "2024-11-21 23:59:59", + "is_enabled": True, + "scope_type": "ip", + "dimension_config": { + "bk_target_ip": [{"bk_host_id": 1, "bk_target_ip": "127.0.0.1", "bk_target_cloud_id": 0}] + }, + "content": "", + "cycle_config": {"type": 1, "week_list": [], "day_list": [], "begin_time": "", "end_time": ""}, + "shield_notice": False, + "notice_config": "{}", + "description": "xxxx", + "source": "", + "update_user": "admin", + }, +] diff --git a/dbm-ui/backend/db_monitor/serializers.py b/dbm-ui/backend/db_monitor/serializers.py index da6a1b58c9..112c558ddb 100644 --- a/dbm-ui/backend/db_monitor/serializers.py +++ b/dbm-ui/backend/db_monitor/serializers.py @@ -256,3 +256,56 @@ def to_internal_value(self, data): data.update({"ticket_types": ticket_types, "creator": "bkmonitor"}) return data + + +class CreateAlarmShieldSerializer(serializers.Serializer): + category = serializers.CharField(help_text=_("屏蔽类型")) + bk_biz_id = serializers.IntegerField(help_text=_("业务ID"), required=True) + dimension_config = serializers.DictField(help_text=_("屏蔽维度配置")) + shield_notice = serializers.BaseSerializer(help_text=_("告警屏蔽通知"), default=False) + begin_time = serializers.CharField(help_text=_("开始时间")) + end_time = serializers.CharField(help_text=_("结束时间")) + description = serializers.CharField(help_text=_("屏蔽原因")) + + def to_internal_value(self, data): + return data + + def validate(self, attrs): + # 取维度中的 appid 维度作为业务,这里要求屏蔽策略的维度一定要有业务 + for condition in attrs["dimension_config"]["dimension_conditions"]: + if condition["key"] == "appid": + attrs["bk_biz_id"] = condition["value"][0] + if "bk_biz_id" not in attrs: + raise serializers.ValidationError(_("维度配置中必须包含业务ID")) + return attrs + + class Meta: + swagger_schema_fields = {"example": mock_data.CREATE_ALARM_SHIELD_FOR_DIMENSION} + + +class UpdateAlarmShieldSerializer(serializers.Serializer): + begin_time = serializers.CharField(help_text=_("开始时间")) + end_time = serializers.CharField(help_text=_("结束时间")) + description = serializers.CharField(help_text=_("屏蔽原因")) + cycle_config = serializers.DictField(help_text=_("屏蔽周期")) + shield_notice = serializers.BooleanField(help_text=_("是否有屏蔽通知"), default=False) + + class Meta: + swagger_schema_fields = {"example": mock_data.UPDATE_ALARM_SHIELD} + + +class DisableAlarmShieldSerializer(serializers.Serializer): + id = serializers.IntegerField(help_text=_("屏蔽 ID")) + + +class ListAlarmShieldSerializer(serializers.Serializer): + bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) + is_active = serializers.BooleanField(help_text=_("是否生效"), default=True) + time_range = serializers.CharField(help_text=_("时间范围"), required=False) + page = serializers.IntegerField(help_text=_("页码"), default=1) + page_size = serializers.IntegerField(help_text=_("每页数量"), default=10) + category = serializers.CharField(help_text=_("屏蔽类型"), required=False) + conditions = serializers.ListSerializer(help_text=_("查询条件"), child=serializers.DictField(), required=False) + + class Meta: + swagger_schema_fields = {"example": mock_data.LIST_ALARM_SHIELD} diff --git a/dbm-ui/backend/db_monitor/urls.py b/dbm-ui/backend/db_monitor/urls.py index 84c98c6ef7..9f5ebd4376 100644 --- a/dbm-ui/backend/db_monitor/urls.py +++ b/dbm-ui/backend/db_monitor/urls.py @@ -14,6 +14,7 @@ from backend.db_monitor.views.grafana import MonitorGrafanaViewSet from backend.db_monitor.views.notice_group import MonitorNoticeGroupViewSet from backend.db_monitor.views.policy import MonitorPolicyViewSet +from backend.db_monitor.views.shield import AlarmShieldView routers = DefaultRouter(trailing_slash=True) @@ -21,5 +22,6 @@ routers.register(r"policy", MonitorPolicyViewSet, basename="policy") routers.register(r"notice_group", MonitorNoticeGroupViewSet, basename="notice_group") routers.register(r"duty_rule", MonitorDutyRuleViewSet, basename="duty_rule") +routers.register(r"alarm_shield", AlarmShieldView, basename="alarm_shield") urlpatterns = routers.urls diff --git a/dbm-ui/backend/db_monitor/utils.py b/dbm-ui/backend/db_monitor/utils.py index 39398ad465..6cd215dc21 100644 --- a/dbm-ui/backend/db_monitor/utils.py +++ b/dbm-ui/backend/db_monitor/utils.py @@ -217,3 +217,10 @@ def create_bklog_collector(startswith: str = ""): logger.error(_("采集项创建失败,请联系管理员。错误信息:{err}").format(err=err)) return True + + +def format_shield_description(bk_biz_id, description=""): + prefix = f"[dbm:appid={bk_biz_id}]" + # 先删后补,避免出现多个前缀 + description.replace(prefix, "").strip() + return f"{prefix}{description}" diff --git a/dbm-ui/backend/db_monitor/views/shield.py b/dbm-ui/backend/db_monitor/views/shield.py new file mode 100644 index 0000000000..8d84c58c51 --- /dev/null +++ b/dbm-ui/backend/db_monitor/views/shield.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at https://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from django.utils.translation import gettext as _ +from rest_framework.decorators import action +from rest_framework.response import Response + +from backend import env +from backend.bk_web.swagger import common_swagger_auto_schema +from backend.bk_web.viewsets import SystemViewSet +from backend.components import BKMonitorV3Api +from backend.db_monitor import serializers +from backend.db_monitor.constants import SWAGGER_TAG +from backend.db_monitor.utils import format_shield_description +from backend.iam_app.handlers.drf_perm.base import DBManagePermission, RejectPermission + + +class AlarmShieldView(SystemViewSet): + def _get_custom_permissions(self): + if self.action == "list": + return [DBManagePermission()] + elif self.action == "create": + return [DBManagePermission()] + elif self.action in ["disable", "update"]: + return [DBManagePermission()] + return [RejectPermission()] + + def get_serializer_class(self): + action_slz_map = { + "list": serializers.ListAlarmShieldSerializer, + "create": serializers.CreateAlarmShieldSerializer, + "update": serializers.UpdateAlarmShieldSerializer, + "disable": serializers.DisableAlarmShieldSerializer, + } + return action_slz_map.get(self.action) + + @common_swagger_auto_schema( + operation_summary=_("告警屏蔽列表"), + query_serializer=serializers.ListAlarmShieldSerializer(), + tags=[SWAGGER_TAG], + ) + def list(self, request): + data = self.validated_data + data.update( + { + "bk_biz_id": env.DBA_APP_BK_BIZ_ID, + "conditions": [{"key": "description", "value": format_shield_description(data["bk_biz_id"])}], + } + ) + return Response(BKMonitorV3Api.list_shield(data)) + + @common_swagger_auto_schema( + operation_summary=_("新增告警屏蔽"), + request_body=serializers.CreateAlarmShieldSerializer(), + tags=[SWAGGER_TAG], + ) + def create(self, request): + data = self.validated_data + data.update( + { + "bk_biz_id": env.DBA_APP_BK_BIZ_ID, + "description": format_shield_description(data["bk_biz_id"], description=data["description"]), + } + ) + return Response(BKMonitorV3Api.add_shield(data)) + + @common_swagger_auto_schema( + operation_summary=_("解除告警屏蔽"), + request_body=serializers.DisableAlarmShieldSerializer(), + tags=[SWAGGER_TAG], + ) + @action(detail=True, methods=["POST"]) + def disable(self, request, pk): + return Response(BKMonitorV3Api.disable_shield({"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "id": pk})) + + @common_swagger_auto_schema( + operation_summary=_("编辑告警屏蔽"), + request_body=serializers.UpdateAlarmShieldSerializer(), + tags=[SWAGGER_TAG], + ) + def update(self, request, pk): + shield = BKMonitorV3Api.get_shield({"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "id": pk}) + data = self.validated_data + data.update( + { + "bk_biz_id": env.DBA_APP_BK_BIZ_ID, + "id": pk, + "description": format_shield_description(shield["bk_biz_id"], description=data["description"]), + } + ) + return Response(BKMonitorV3Api.edit_shield(data))