From a6c3f92c1822e2ab1d1138e9ed4840d051b8902b Mon Sep 17 00:00:00 2001 From: iSecloud <869820505@qq.com> Date: Mon, 13 Jan 2025 15:26:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(backend):=20=E5=B7=A1=E6=A3=80=E9=80=9A?= =?UTF-8?q?=E7=94=A8=E6=A1=86=E6=9E=B6=20#9120?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dbm-ui/backend/configuration/models/dba.py | 19 ++++- dbm-ui/backend/db_report/README.md | 4 + dbm-ui/backend/db_report/ReadMe.md | 4 - dbm-ui/backend/db_report/apps.py | 15 ++++ dbm-ui/backend/db_report/enums/__init__.py | 14 ++++ dbm-ui/backend/db_report/filters.py | 46 ++++++++++++ dbm-ui/backend/db_report/mock_data.py | 20 +++++ dbm-ui/backend/db_report/register.py | 34 +++++++++ dbm-ui/backend/db_report/report_baseview.py | 75 +++++++++++++++++-- ...ck_report_serializer.py => serializers.py} | 12 ++- dbm-ui/backend/db_report/urls.py | 29 +++---- dbm-ui/backend/db_report/views/__init__.py | 9 +-- .../views/checksum_check_report_view.py | 11 +-- .../db_report/views/checksum_instance_view.py | 4 +- .../db_report/views/meta_check_view.py | 18 ++--- .../views/mysql/mysql_checksum_report_view.py | 23 ++++++ .../mysql/mysql_dbmeta_check_view.py} | 14 ++++ .../{ => mysql}/mysqlbackup_check_view.py | 20 ++--- .../views/{ => redis}/dbmon_heartbeat_view.py | 16 ++-- .../{ => redis}/redis_dbmeta_check_view.py | 20 ++--- .../{ => redis}/redisbackup_check_view.py | 21 ++---- dbm-ui/backend/ticket/builders/__init__.py | 19 +---- dbm-ui/backend/ticket/todos/__init__.py | 19 +---- dbm-ui/backend/urls.py | 1 + dbm-ui/backend/utils/register.py | 33 ++++++++ 25 files changed, 360 insertions(+), 140 deletions(-) create mode 100644 dbm-ui/backend/db_report/README.md delete mode 100644 dbm-ui/backend/db_report/ReadMe.md create mode 100644 dbm-ui/backend/db_report/filters.py create mode 100644 dbm-ui/backend/db_report/register.py rename dbm-ui/backend/db_report/{serializers/meta_check_report_serializer.py => serializers.py} (71%) create mode 100644 dbm-ui/backend/db_report/views/mysql/mysql_checksum_report_view.py rename dbm-ui/backend/db_report/{serializers/__init__.py => views/mysql/mysql_dbmeta_check_view.py} (53%) rename dbm-ui/backend/db_report/views/{ => mysql}/mysqlbackup_check_view.py (89%) rename dbm-ui/backend/db_report/views/{ => redis}/dbmon_heartbeat_view.py (89%) rename dbm-ui/backend/db_report/views/{ => redis}/redis_dbmeta_check_view.py (89%) rename dbm-ui/backend/db_report/views/{ => redis}/redisbackup_check_view.py (89%) create mode 100644 dbm-ui/backend/utils/register.py diff --git a/dbm-ui/backend/configuration/models/dba.py b/dbm-ui/backend/configuration/models/dba.py index 04e0c65bf4..69afcdad52 100644 --- a/dbm-ui/backend/configuration/models/dba.py +++ b/dbm-ui/backend/configuration/models/dba.py @@ -8,7 +8,7 @@ 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 typing import Dict, List, Union +from typing import Dict, List, Tuple, Union from django.db import models from django.utils.translation import ugettext_lazy as _ @@ -62,9 +62,24 @@ def get_biz_db_type_admins(cls, bk_biz_id: int, db_type: str) -> List[str]: return DEFAULT_DB_ADMINISTRATORS @classmethod - def get_dba_for_db_type(cls, bk_biz_id: int, db_type: str) -> List[str]: + def get_dba_for_db_type(cls, bk_biz_id: int, db_type: str) -> Tuple[List[str], List[str], List[str]]: """获取主dba、备dba、二线dba人员""" dba_list = cls.list_biz_admins(bk_biz_id) dba_content = next((dba for dba in dba_list if dba["db_type"] == db_type), {"users": []}) users = dba_content.get("users", []) return users[:1], users[1:2], users[2:] + + @classmethod + def get_manage_bizs(cls, db_type: str, username: str) -> Tuple[List[str], List[str]]: + """获取待我处理,待我协助的业务""" + manage_biz = DBAdministrator.objects.filter(db_type=db_type, users__0=username).values_list( + "bk_biz_id", flat=True + ) + + assist_bizs = ( + DBAdministrator.objects.filter(db_type=db_type, users__contains=username) + .exclude(users__0=username) + .values_list("bk_biz_id", flat=True) + ) + + return list(manage_biz), list(assist_bizs) diff --git a/dbm-ui/backend/db_report/README.md b/dbm-ui/backend/db_report/README.md new file mode 100644 index 0000000000..92bfc8f2b2 --- /dev/null +++ b/dbm-ui/backend/db_report/README.md @@ -0,0 +1,4 @@ +1. 报告结果 _model_ 继承 `BaseReportABS` +2. 报告结果视图继承 `ReportBaseViewSet` +3. 视图 +3. 示例可以参考 `dbm-ui/backend/db_report/views/mysql/mysqlbackup_check_view.py` \ No newline at end of file diff --git a/dbm-ui/backend/db_report/ReadMe.md b/dbm-ui/backend/db_report/ReadMe.md deleted file mode 100644 index d13b18391a..0000000000 --- a/dbm-ui/backend/db_report/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -1. 报告结果 _model_ 继承 `BaseReportABS` -2. 报告结果视图继承 `ReportBaseViewSet` -3. 在 `dbm-ui/backend/db_report/urls.py` 中注册 -4. 示例可以参考 `dbm-ui/backend/db_report/views/meta_check_view.py` \ No newline at end of file diff --git a/dbm-ui/backend/db_report/apps.py b/dbm-ui/backend/db_report/apps.py index 4b98cd720f..9c076f7d77 100644 --- a/dbm-ui/backend/db_report/apps.py +++ b/dbm-ui/backend/db_report/apps.py @@ -1,6 +1,21 @@ +""" +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.apps import AppConfig +from backend.db_report.register import register_all_reports + class DbReportConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "backend.db_report" + + def ready(self): + register_all_reports() diff --git a/dbm-ui/backend/db_report/enums/__init__.py b/dbm-ui/backend/db_report/enums/__init__.py index 220cfc1663..309183b9d7 100644 --- a/dbm-ui/backend/db_report/enums/__init__.py +++ b/dbm-ui/backend/db_report/enums/__init__.py @@ -19,9 +19,23 @@ SWAGGER_TAG = _("巡检报告") +REPORT_COUNT_CACHE_KEY = "{user}_report_count_key" + class ReportFieldFormat(str, StructuredEnum): TEXT = EnumField("text", _("文本渲染")) STATUS = EnumField("status", _("状态渲染")) # 数据校验失败详情字段 FAIL_SLAVE_INSTANCE = EnumField("fail_slave_instance", _("数据校验失败详情渲染")) + + +class ReportType(str, StructuredEnum): + CHECKSUM = EnumField("checksum", _("数据校验")) + FULL_BACKUP_CHECK = EnumField("full_backup_check", _("全备校验")) + BINLOG_BACKUP_CHECK = EnumField("binlog_backup_check", _("集群binlog检查")) + + ALONE_INSTANCE_CHECK = EnumField("alone_instance_check", _("孤立实例检查")) + STATUS_ABNORMAL_CHECK = EnumField("status_abnormal_check", _("实例异常状态检查")) + META_CHECK = EnumField("meta_check", _("元数据检查")) + + REDIS_DBMON_HEARTBEAT_CHECK = EnumField("dbmon_heartbeat_check", _("dbmon心跳超时检查")) diff --git a/dbm-ui/backend/db_report/filters.py b/dbm-ui/backend/db_report/filters.py new file mode 100644 index 0000000000..da726bec5b --- /dev/null +++ b/dbm-ui/backend/db_report/filters.py @@ -0,0 +1,46 @@ +# -*- 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 ugettext_lazy as _ +from django_filters import rest_framework as filters +from django_filters.rest_framework import DjangoFilterBackend + +from backend.configuration.models import DBAdministrator + + +class ReportListFilter(filters.FilterSet): + manage = filters.CharFilter( + field_name="manage", method="filter_manage", label=_("处理类型"), help_text=_("todo待我处理/assist待我协助") + ) + cluster = filters.CharFilter(field_name="cluster", method="filter_cluster", label=_("集群名")) + + def filter_manage(self, queryset, name, value): + username = self.request.user.username + db_type = self.request.path.strip("/").split("/")[1] + manage_bizs, assist_bizs = DBAdministrator.get_manage_bizs(db_type, username) + # 待我处理 + if value == "todo": + return queryset.filter(bk_biz_id__in=manage_bizs) + # 待我协助 + elif value == "assist": + return queryset.filter(bk_biz_id__in=assist_bizs) + # 其他情况忽略 + return queryset + + def filter_cluster(self, queryset, name, value): + cluster = value.split(",") + if len(cluster) == 1: + return queryset.filter(cluster__icontains=cluster[0]) + else: + return queryset.filter(cluster__in=cluster) + + +class ReportFilterBackend(DjangoFilterBackend): + filterset_base = ReportListFilter diff --git a/dbm-ui/backend/db_report/mock_data.py b/dbm-ui/backend/db_report/mock_data.py index d4907bf571..a0f2cddd62 100644 --- a/dbm-ui/backend/db_report/mock_data.py +++ b/dbm-ui/backend/db_report/mock_data.py @@ -144,3 +144,23 @@ {"name": "msg", "display_name": "详情", "format": "text"}, ], } + +REPORT_OVERVIEW_DATA = { + "redis": [ + "dbmon_heartbeat_check", + "full_backup_check", + "binlog_backup_check", + "alone_instance_check", + "status_abnormal_check", + ], + "mysql": ["full_backup_check", "binlog_backup_check", "meta_check", "checksum"], +} + +REPORT_COUNT_DATA = { + "redis": { + "dbmon_heartbeat_check": {"manage_count": 10896, "assist_count": 0}, + }, + "mysql": { + "full_backup_check": {"manage_count": 0, "assist_count": 26}, + }, +} diff --git a/dbm-ui/backend/db_report/register.py b/dbm-ui/backend/db_report/register.py new file mode 100644 index 0000000000..9f06e0a9bf --- /dev/null +++ b/dbm-ui/backend/db_report/register.py @@ -0,0 +1,34 @@ +""" +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. +""" +import logging +import os +from collections import defaultdict +from typing import Dict, List + +from backend.utils.register import re_import_modules +from config import BASE_DIR + +logger = logging.getLogger("root") + +db_report_maps: Dict[str, List] = defaultdict(list) + + +def register_report(db_type): + """巡检视图的注册器""" + + def decorator(report_cls): + db_report_maps[db_type].append(report_cls) + + return decorator + + +def register_all_reports(): + """递归注册当前目录下所有的巡检报告""" + re_import_modules(path=os.path.join(BASE_DIR, "backend/db_report/views"), module_path="backend.db_report.views") diff --git a/dbm-ui/backend/db_report/report_baseview.py b/dbm-ui/backend/db_report/report_baseview.py index 6173ac5b28..ab0a0738fd 100644 --- a/dbm-ui/backend/db_report/report_baseview.py +++ b/dbm-ui/backend/db_report/report_baseview.py @@ -8,29 +8,47 @@ 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 rest_framework import mixins +from collections import defaultdict +from typing import Dict + +from django.core.cache import cache +from django.utils.translation import gettext_lazy as _ +from rest_framework import mixins, status +from rest_framework.decorators import action +from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet +from backend.bk_web import viewsets from backend.bk_web.pagination import AuditedLimitOffsetPagination +from backend.bk_web.swagger import common_swagger_auto_schema +from backend.configuration.models import DBAdministrator +from backend.db_report.enums import REPORT_COUNT_CACHE_KEY, SWAGGER_TAG, ReportType +from backend.db_report.filters import ReportFilterBackend +from backend.db_report.register import db_report_maps +from backend.db_report.serializers import GetReportCountSerializer, GetReportOverviewSerializer from backend.iam_app.dataclass import ResourceEnum from backend.iam_app.dataclass.actions import ActionEnum from backend.iam_app.handlers.drf_perm.base import ResourceActionPermission, get_request_key_id class ReportBaseViewSet(GenericViewSet, mixins.ListModelMixin): + # 分页类 pagination_class = AuditedLimitOffsetPagination - + # 巡检类型 + report_type = None + # 巡检名称 + report_name = "" + # 巡检表头 + report_title = [] + # 巡检过滤类 + filter_backends = [ReportFilterBackend] filter_fields = { "bk_biz_id": ["exact"], - "cluster": ["exact", "in"], "cluster_type": ["exact", "in"], "create_at": ["gte", "lte"], "status": ["exact", "in"], } - report_name = "" - report_title = [] - @staticmethod def instance_getter(request, view): return [get_request_key_id(request, "bk_biz_id")] @@ -42,7 +60,48 @@ def get_permissions(self): def list(self, request, *args, **kwargs): response = super().list(request, *args, **kwargs) - response.data["name"] = self.report_name + response.data["name"] = self.report_name or ReportType.get_choice_label(self.report_type) response.data["title"] = self.report_title - return response + + +class ReportCommonViewSet(viewsets.SystemViewSet): + """巡检通用接口视图""" + + @common_swagger_auto_schema( + operation_summary=_("获取巡检报告总览"), + responses={status.HTTP_200_OK: GetReportOverviewSerializer()}, + tags=[SWAGGER_TAG], + ) + @action(methods=["GET"], detail=False, serializer_class=GetReportOverviewSerializer) + def get_report_overview(self, request, *args, **kwargs): + db_report_types = defaultdict(list) + for db_type, report_cls_list in db_report_maps.items(): + db_report_types[db_type] = [cls.report_type for cls in report_cls_list] + return Response(db_report_types) + + @common_swagger_auto_schema( + operation_summary=_("获取巡检报告代办数量"), + responses={status.HTTP_200_OK: GetReportCountSerializer()}, + tags=[SWAGGER_TAG], + ) + @action(methods=["GET"], detail=False, serializer_class=GetReportCountSerializer) + def get_report_count(self, request, *args, **kwargs): + username = request.user.username + cache_key = REPORT_COUNT_CACHE_KEY.format(user=username) + + report_count_cache = cache.get(cache_key) + if report_count_cache: + return Response(report_count_cache) + else: + report_count_map: Dict[str, Dict[str, Dict]] = defaultdict(lambda: defaultdict(dict)) + for db_type, report_classes in db_report_maps.items(): + manage_bizs, assist_bizs = DBAdministrator.get_manage_bizs(db_type, username) + for cls in report_classes: + report_count_map[db_type][cls.report_type].update( + manage_count=cls.queryset.filter(status=False, bk_biz_id__in=manage_bizs).count(), + assist_count=cls.queryset.filter(status=False, bk_biz_id__in=assist_bizs).count(), + ) + # 数量精确性不高,可以做1h的缓存 + cache.set(cache_key, report_count_map, 60 * 60) + return Response(report_count_map) diff --git a/dbm-ui/backend/db_report/serializers/meta_check_report_serializer.py b/dbm-ui/backend/db_report/serializers.py similarity index 71% rename from dbm-ui/backend/db_report/serializers/meta_check_report_serializer.py rename to dbm-ui/backend/db_report/serializers.py index 37e1436a30..467807fb7b 100644 --- a/dbm-ui/backend/db_report/serializers/meta_check_report_serializer.py +++ b/dbm-ui/backend/db_report/serializers.py @@ -10,10 +10,14 @@ """ from rest_framework import serializers -from backend.db_report.models import MetaCheckReport +from backend.db_report import mock_data -class MetaCheckReportSerializer(serializers.ModelSerializer): +class GetReportOverviewSerializer(serializers.Serializer): class Meta: - model = MetaCheckReport - fields = "__all__" + swagger_schema_fields = {"example": mock_data.REPORT_OVERVIEW_DATA} + + +class GetReportCountSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": mock_data.REPORT_COUNT_DATA} diff --git a/dbm-ui/backend/db_report/urls.py b/dbm-ui/backend/db_report/urls.py index 18d0081fa1..f1b908c0a1 100644 --- a/dbm-ui/backend/db_report/urls.py +++ b/dbm-ui/backend/db_report/urls.py @@ -8,19 +8,20 @@ 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.conf.urls import url +from rest_framework.routers import DefaultRouter -from backend.db_report import views +from backend.db_report.register import db_report_maps +from backend.db_report.report_baseview import ReportCommonViewSet +from backend.db_report.views.checksum_instance_view import ChecksumInstanceViewSet -urlpatterns = [ - url("^meta_check/instance_belong$", views.MetaCheckReportInstanceBelongViewSet.as_view({"get": "list"})), - url("^checksum_check/report$", views.ChecksumCheckReportViewSet.as_view({"get": "list"})), - url("^checksum_check/instance$", views.ChecksumInstanceViewSet.as_view({"get": "list"})), - url("^mysql_check/full_backup$", views.MysqlFullBackupCheckReportViewSet.as_view({"get": "list"})), - url("^mysql_check/binlog_backup$", views.MysqlBinlogBackupCheckReportViewSet.as_view({"get": "list"})), - url("^redis_check/full_backup$", views.RedisFullBackupCheckReportViewSet.as_view({"get": "list"})), - url("^redis_check/binlog_backup$", views.RedisBinlogBackupCheckReportViewSet.as_view({"get": "list"})), - url("^dbmon/heartbeat$", views.DbmonHeatbeartCheckReportBaseViewSet.as_view({"get": "list"})), - url("^redis_meta_check/status_abnormal$", views.RedisStatusAbnormalCheckReportViewSet.as_view({"get": "list"})), - url("^redis_meta_check/alone_instance$", views.RedisAloneInstanceCheckReportViewSet.as_view({"get": "list"})), -] +routers = DefaultRouter(trailing_slash=True) + +routers.register(r"", ReportCommonViewSet, basename="report_common") +routers.register(r"checksum_instance", ChecksumInstanceViewSet, basename="checksum_instance") + +# 自动添加注册的巡检视图 +for db_type, reports in db_report_maps.items(): + for report in reports: + routers.register(f"{db_type}/{report.report_type}", report, basename=f"{db_type}-{report.report_type}") + +urlpatterns = routers.urls diff --git a/dbm-ui/backend/db_report/views/__init__.py b/dbm-ui/backend/db_report/views/__init__.py index 2bf74b43f2..775d4ec623 100644 --- a/dbm-ui/backend/db_report/views/__init__.py +++ b/dbm-ui/backend/db_report/views/__init__.py @@ -8,10 +8,5 @@ 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 .checksum_check_report_view import ChecksumCheckReportViewSet -from .checksum_instance_view import ChecksumInstanceViewSet -from .dbmon_heartbeat_view import DbmonHeatbeartCheckReportBaseViewSet -from .meta_check_view import MetaCheckReportInstanceBelongViewSet -from .mysqlbackup_check_view import MysqlBinlogBackupCheckReportViewSet, MysqlFullBackupCheckReportViewSet -from .redis_dbmeta_check_view import RedisAloneInstanceCheckReportViewSet, RedisStatusAbnormalCheckReportViewSet -from .redisbackup_check_view import RedisBinlogBackupCheckReportViewSet, RedisFullBackupCheckReportViewSet +from .mysql import * +from .redis import * diff --git a/dbm-ui/backend/db_report/views/checksum_check_report_view.py b/dbm-ui/backend/db_report/views/checksum_check_report_view.py index 5a24b95e3a..79905f5f77 100644 --- a/dbm-ui/backend/db_report/views/checksum_check_report_view.py +++ b/dbm-ui/backend/db_report/views/checksum_check_report_view.py @@ -12,7 +12,7 @@ import logging -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext as _ from rest_framework import serializers, status from backend.bk_web.swagger import common_swagger_auto_schema @@ -31,16 +31,9 @@ class Meta: swagger_schema_fields = {"example": mock_data.CHECKSUM_CHECK_DATA} -class ChecksumCheckReportViewSet(ReportBaseViewSet): +class ChecksumCheckReportBaseViewSet(ReportBaseViewSet): queryset = ChecksumCheckReport.objects.all().order_by("-create_at") serializer_class = ChecksumCheckReportSerializer - filter_fields = { - "bk_biz_id": ["exact"], - "cluster_type": ["exact"], - "create_at": ["gte", "lte"], - "status": ["exact", "in"], - "cluster": ["exact", "in"], - } report_name = _("数据校验") report_title = [ { diff --git a/dbm-ui/backend/db_report/views/checksum_instance_view.py b/dbm-ui/backend/db_report/views/checksum_instance_view.py index 744943bb57..e9447ea3af 100644 --- a/dbm-ui/backend/db_report/views/checksum_instance_view.py +++ b/dbm-ui/backend/db_report/views/checksum_instance_view.py @@ -12,7 +12,8 @@ import logging -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext as _ +from django_filters.rest_framework import DjangoFilterBackend from rest_framework import serializers, status from backend.bk_web.swagger import common_swagger_auto_schema @@ -32,6 +33,7 @@ class Meta: class ChecksumInstanceViewSet(ReportBaseViewSet): queryset = ChecksumInstance.objects.all() serializer_class = ChecksumInstanceSerializer + filter_backends = [DjangoFilterBackend] filter_fields = { "report_id": ["exact"], } diff --git a/dbm-ui/backend/db_report/views/meta_check_view.py b/dbm-ui/backend/db_report/views/meta_check_view.py index 5ed61348b9..c1da0c0e6e 100644 --- a/dbm-ui/backend/db_report/views/meta_check_view.py +++ b/dbm-ui/backend/db_report/views/meta_check_view.py @@ -12,7 +12,7 @@ import logging -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext as _ from rest_framework import serializers, status from backend.bk_web.swagger import common_swagger_auto_schema @@ -24,23 +24,17 @@ logger = logging.getLogger("root") -class MetaCheckReportInstanceBelongSerializer(serializers.ModelSerializer): +class MetaCheckReportSerializer(serializers.ModelSerializer): class Meta: model = MetaCheckReport fields = ("bk_biz_id", "ip", "port", "machine_type", "status", "msg", "create_at") swagger_schema_fields = {"example": mock_data.META_CHECK_DATA} -class MetaCheckReportInstanceBelongViewSet(ReportBaseViewSet): +class MetaCheckReportBaseViewSet(ReportBaseViewSet): queryset = MetaCheckReport.objects.all() - serializer_class = MetaCheckReportInstanceBelongSerializer - filter_fields = { # 大部分时候不需要覆盖默认的filter - "bk_biz_id": ["exact"], - "cluster_type": ["exact", "in"], - "create_at": ["gte", "lte"], - "status": ["exact", "in"], - } - report_name = _("实例集群归属") + serializer_class = MetaCheckReportSerializer + report_name = _("元数据检查") report_title = [ { "name": "bk_biz_id", @@ -81,7 +75,7 @@ class MetaCheckReportInstanceBelongViewSet(ReportBaseViewSet): @common_swagger_auto_schema( operation_summary=_("元数据检查报告列表"), - responses={status.HTTP_200_OK: MetaCheckReportInstanceBelongSerializer()}, + responses={status.HTTP_200_OK: MetaCheckReportSerializer()}, tags=[SWAGGER_TAG], ) def list(self, request, *args, **kwargs): diff --git a/dbm-ui/backend/db_report/views/mysql/mysql_checksum_report_view.py b/dbm-ui/backend/db_report/views/mysql/mysql_checksum_report_view.py new file mode 100644 index 0000000000..2c7407293a --- /dev/null +++ b/dbm-ui/backend/db_report/views/mysql/mysql_checksum_report_view.py @@ -0,0 +1,23 @@ +# -*- 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 backend.configuration.constants import DBType +from backend.db_meta.enums import ClusterType +from backend.db_report.enums import ReportType +from backend.db_report.models import ChecksumCheckReport +from backend.db_report.register import register_report +from backend.db_report.views.checksum_check_report_view import ChecksumCheckReportBaseViewSet + + +@register_report(DBType.MySQL) +class MySQLChecksumCheckReportViewSet(ChecksumCheckReportBaseViewSet): + report_type = ReportType.CHECKSUM + cluster_types = ClusterType.db_type_to_cluster_types(DBType.MySQL) + queryset = ChecksumCheckReport.objects.filter(cluster_type__in=cluster_types).order_by("-create_at") diff --git a/dbm-ui/backend/db_report/serializers/__init__.py b/dbm-ui/backend/db_report/views/mysql/mysql_dbmeta_check_view.py similarity index 53% rename from dbm-ui/backend/db_report/serializers/__init__.py rename to dbm-ui/backend/db_report/views/mysql/mysql_dbmeta_check_view.py index aa5085c628..fb6a22cc4a 100644 --- a/dbm-ui/backend/db_report/serializers/__init__.py +++ b/dbm-ui/backend/db_report/views/mysql/mysql_dbmeta_check_view.py @@ -8,3 +8,17 @@ 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 backend.configuration.constants import DBType +from backend.db_meta.enums import ClusterType +from backend.db_report.enums import ReportType +from backend.db_report.models import MetaCheckReport +from backend.db_report.register import register_report +from backend.db_report.views.meta_check_view import MetaCheckReportBaseViewSet + + +@register_report(DBType.MySQL) +class MySQLMetaCheckReportViewSet(MetaCheckReportBaseViewSet): + report_type = ReportType.META_CHECK + cluster_types = ClusterType.db_type_to_cluster_types(DBType.MySQL) + queryset = MetaCheckReport.objects.filter(cluster_type__in=cluster_types).order_by("-create_at") diff --git a/dbm-ui/backend/db_report/views/mysqlbackup_check_view.py b/dbm-ui/backend/db_report/views/mysql/mysqlbackup_check_view.py similarity index 89% rename from dbm-ui/backend/db_report/views/mysqlbackup_check_view.py rename to dbm-ui/backend/db_report/views/mysql/mysqlbackup_check_view.py index 5eb75d4c06..9503cdf96e 100644 --- a/dbm-ui/backend/db_report/views/mysqlbackup_check_view.py +++ b/dbm-ui/backend/db_report/views/mysql/mysqlbackup_check_view.py @@ -12,13 +12,15 @@ import logging -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext as _ from rest_framework import serializers, status from backend.bk_web.swagger import common_swagger_auto_schema +from backend.configuration.constants import DBType from backend.db_report import mock_data -from backend.db_report.enums import SWAGGER_TAG, MysqlBackupCheckSubType, ReportFieldFormat +from backend.db_report.enums import SWAGGER_TAG, MysqlBackupCheckSubType, ReportFieldFormat, ReportType from backend.db_report.models import MysqlBackupCheckReport +from backend.db_report.register import register_report from backend.db_report.report_baseview import ReportBaseViewSet logger = logging.getLogger("root") @@ -34,14 +36,6 @@ class Meta: class MysqlBackupCheckReportBaseViewSet(ReportBaseViewSet): queryset = MysqlBackupCheckReport.objects.all() serializer_class = MysqlBackupCheckReportSerializer - filter_fields = { # 大部分时候不需要覆盖默认的filter - "bk_biz_id": ["exact"], - "cluster": ["exact", "in"], - "cluster_type": ["exact", "in"], - "create_at": ["gte", "lte"], - "status": ["exact", "in"], - } - report_name = _("集群全备检查") report_title = [ { "name": "bk_biz_id", @@ -85,10 +79,11 @@ def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs) +@register_report(DBType.MySQL) class MysqlFullBackupCheckReportViewSet(MysqlBackupCheckReportBaseViewSet): queryset = MysqlBackupCheckReport.objects.filter(subtype=MysqlBackupCheckSubType.FullBackup.value) serializer_class = MysqlBackupCheckReportSerializer - report_name = _("MySQL 全备检查") + report_type = ReportType.FULL_BACKUP_CHECK @common_swagger_auto_schema( operation_summary=_("MySQL 全备检查报告"), @@ -99,10 +94,11 @@ def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs) +@register_report(DBType.MySQL) class MysqlBinlogBackupCheckReportViewSet(MysqlBackupCheckReportBaseViewSet): queryset = MysqlBackupCheckReport.objects.filter(subtype=MysqlBackupCheckSubType.BinlogSeq.value) serializer_class = MysqlBackupCheckReportSerializer - report_name = _("集群binlog检查") + report_type = ReportType.BINLOG_BACKUP_CHECK @common_swagger_auto_schema( operation_summary=_("MySQL binlog检查报告"), diff --git a/dbm-ui/backend/db_report/views/dbmon_heartbeat_view.py b/dbm-ui/backend/db_report/views/redis/dbmon_heartbeat_view.py similarity index 89% rename from dbm-ui/backend/db_report/views/dbmon_heartbeat_view.py rename to dbm-ui/backend/db_report/views/redis/dbmon_heartbeat_view.py index 736c70327a..57c56c88d5 100644 --- a/dbm-ui/backend/db_report/views/dbmon_heartbeat_view.py +++ b/dbm-ui/backend/db_report/views/redis/dbmon_heartbeat_view.py @@ -12,13 +12,15 @@ import logging -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext as _ from rest_framework import serializers, status from backend.bk_web.swagger import common_swagger_auto_schema +from backend.configuration.constants import DBType from backend.db_report import mock_data -from backend.db_report.enums import SWAGGER_TAG, ReportFieldFormat +from backend.db_report.enums import SWAGGER_TAG, ReportFieldFormat, ReportType from backend.db_report.models import DbmonHeartbeatReport +from backend.db_report.register import register_report from backend.db_report.report_baseview import ReportBaseViewSet logger = logging.getLogger("root") @@ -31,17 +33,11 @@ class Meta: swagger_schema_fields = {"example": mock_data.DBMON_HEARTBEAT_CHECK_DATA} +@register_report(DBType.Redis) class DbmonHeatbeartCheckReportBaseViewSet(ReportBaseViewSet): queryset = DbmonHeartbeatReport.objects.all() serializer_class = DbmonHeartbeatCheckReportSerializer - filter_fields = { # 大部分时候不需要覆盖默认的filter - "bk_biz_id": ["exact"], - "cluster_type": ["exact", "in"], - "cluster": ["exact", "in"], - "create_at": ["gte", "lte"], - "status": ["exact", "in"], - } - report_name = _("dbmon心跳超时检查") + report_type = ReportType.REDIS_DBMON_HEARTBEAT_CHECK report_title = [ { "name": "bk_biz_id", diff --git a/dbm-ui/backend/db_report/views/redis_dbmeta_check_view.py b/dbm-ui/backend/db_report/views/redis/redis_dbmeta_check_view.py similarity index 89% rename from dbm-ui/backend/db_report/views/redis_dbmeta_check_view.py rename to dbm-ui/backend/db_report/views/redis/redis_dbmeta_check_view.py index 1c973c06fa..ea751bd43c 100644 --- a/dbm-ui/backend/db_report/views/redis_dbmeta_check_view.py +++ b/dbm-ui/backend/db_report/views/redis/redis_dbmeta_check_view.py @@ -11,13 +11,15 @@ import logging -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext as _ from rest_framework import serializers, status from backend.bk_web.swagger import common_swagger_auto_schema +from backend.configuration.constants import DBType from backend.db_report import mock_data -from backend.db_report.enums import SWAGGER_TAG, MetaCheckSubType, ReportFieldFormat +from backend.db_report.enums import SWAGGER_TAG, MetaCheckSubType, ReportFieldFormat, ReportType from backend.db_report.models import MetaCheckReport +from backend.db_report.register import register_report from backend.db_report.report_baseview import ReportBaseViewSet logger = logging.getLogger("root") @@ -33,14 +35,6 @@ class Meta: class RedisDbmetaCheckReportBaseViewSet(ReportBaseViewSet): queryset = MetaCheckReport.objects.all() serializer_class = RedisDbmetaCheckReportSerializer - filter_fields = { # 大部分时候不需要覆盖默认的filter - "bk_biz_id": ["exact"], - "cluster": ["exact", "in"], - "cluster_type": ["exact", "in"], - "create_at": ["gte", "lte"], - "status": ["exact", "in"], - } - report_name = _("redis 元数据检查") report_title = [ { "name": "bk_biz_id", @@ -84,10 +78,11 @@ def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs) +@register_report(DBType.Redis) class RedisAloneInstanceCheckReportViewSet(RedisDbmetaCheckReportBaseViewSet): queryset = MetaCheckReport.objects.filter(subtype=MetaCheckSubType.AloneInstance.value) serializer_class = RedisDbmetaCheckReportSerializer - report_name = _("孤立节点检查") + report_type = ReportType.ALONE_INSTANCE_CHECK @common_swagger_auto_schema( operation_summary=_("孤立节点检查报告"), @@ -98,10 +93,11 @@ def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs) +@register_report(DBType.Redis) class RedisStatusAbnormalCheckReportViewSet(RedisDbmetaCheckReportBaseViewSet): queryset = MetaCheckReport.objects.filter(subtype=MetaCheckSubType.StatusAbnormal.value) serializer_class = RedisDbmetaCheckReportSerializer - report_name = _("实例状态异常检查") + report_type = ReportType.STATUS_ABNORMAL_CHECK @common_swagger_auto_schema( operation_summary=_("实例状态异常检查"), diff --git a/dbm-ui/backend/db_report/views/redisbackup_check_view.py b/dbm-ui/backend/db_report/views/redis/redisbackup_check_view.py similarity index 89% rename from dbm-ui/backend/db_report/views/redisbackup_check_view.py rename to dbm-ui/backend/db_report/views/redis/redisbackup_check_view.py index 52caecf5f8..c05eb433af 100644 --- a/dbm-ui/backend/db_report/views/redisbackup_check_view.py +++ b/dbm-ui/backend/db_report/views/redis/redisbackup_check_view.py @@ -11,13 +11,15 @@ import logging -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext as _ from rest_framework import serializers, status from backend.bk_web.swagger import common_swagger_auto_schema +from backend.configuration.constants import DBType from backend.db_report import mock_data -from backend.db_report.enums import SWAGGER_TAG, RedisBackupCheckSubType, ReportFieldFormat +from backend.db_report.enums import SWAGGER_TAG, RedisBackupCheckSubType, ReportFieldFormat, ReportType from backend.db_report.models import RedisBackupCheckReport +from backend.db_report.register import register_report from backend.db_report.report_baseview import ReportBaseViewSet logger = logging.getLogger("root") @@ -33,14 +35,6 @@ class Meta: class RedisBackupCheckReportBaseViewSet(ReportBaseViewSet): queryset = RedisBackupCheckReport.objects.all() serializer_class = RedisBackupCheckReportSerializer - filter_fields = { # 大部分时候不需要覆盖默认的filter - "bk_biz_id": ["exact"], - "cluster": ["exact", "in"], - "cluster_type": ["exact", "in"], - "create_at": ["gte", "lte"], - "status": ["exact", "in"], - } - report_name = _("集群备份检查") report_title = [ { "name": "bk_biz_id", @@ -89,10 +83,11 @@ def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs) +@register_report(DBType.Redis) class RedisFullBackupCheckReportViewSet(RedisBackupCheckReportBaseViewSet): queryset = RedisBackupCheckReport.objects.filter(subtype=RedisBackupCheckSubType.FullBackup.value) serializer_class = RedisBackupCheckReportSerializer - report_name = _("Redis 全备检查") + report_type = ReportType.FULL_BACKUP_CHECK @common_swagger_auto_schema( operation_summary=_("Redis 全备检查报告"), @@ -103,11 +98,11 @@ def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs) +@register_report(DBType.Redis) class RedisBinlogBackupCheckReportViewSet(RedisBackupCheckReportBaseViewSet): queryset = RedisBackupCheckReport.objects.filter(subtype=RedisBackupCheckSubType.BinlogBackup.value) - serializer_class = RedisBackupCheckReportSerializer - report_name = _("Redis集群binlog检查") + report_type = ReportType.BINLOG_BACKUP_CHECK @common_swagger_auto_schema( operation_summary=_("Redis binlog检查报告"), diff --git a/dbm-ui/backend/ticket/builders/__init__.py b/dbm-ui/backend/ticket/builders/__init__.py index 77828cd731..7dce423d3b 100644 --- a/dbm-ui/backend/ticket/builders/__init__.py +++ b/dbm-ui/backend/ticket/builders/__init__.py @@ -26,6 +26,7 @@ from backend.iam_app.dataclass.actions import ActionEnum from backend.ticket.constants import TICKET_EXPIRE_DEFAULT_CONFIG, FlowRetryType, FlowType, TicketType from backend.ticket.models import Flow, Ticket, TicketFlowsConfig +from backend.utils.register import re_import_modules logger = logging.getLogger("root") @@ -515,19 +516,5 @@ def create_builder(cls, ticket: Ticket): return builder_cls(ticket) -def register_all_builders(path=os.path.dirname(__file__), module_path="backend.ticket.builders"): - """递归注册当前目录下所有的构建器""" - for name in os.listdir(path): - # 忽略无效文件 - if name.endswith(".pyc") or name in ["__init__.py", "__pycache__"]: - continue - - if os.path.isdir(os.path.join(path, name)): - register_all_builders(os.path.join(path, name), ".".join([module_path, name])) - else: - try: - module_name = name.replace(".py", "") - import_path = ".".join([module_path, module_name]) - importlib.import_module(import_path) - except ModuleNotFoundError as e: - logger.warning(e) +def register_all_builders(): + re_import_modules(path=os.path.dirname(__file__), module_path="backend.ticket.builders") diff --git a/dbm-ui/backend/ticket/todos/__init__.py b/dbm-ui/backend/ticket/todos/__init__.py index 6158196fe4..45d22270d6 100644 --- a/dbm-ui/backend/ticket/todos/__init__.py +++ b/dbm-ui/backend/ticket/todos/__init__.py @@ -21,6 +21,7 @@ from backend.ticket.constants import TODO_RUNNING_STATUS from backend.ticket.exceptions import TodoDuplicateProcessException, TodoWrongOperatorException from backend.ticket.models import Todo +from backend.utils.register import re_import_modules from blue_krill.data_types.enum import EnumField, StructuredEnum logger = logging.getLogger("root") @@ -106,22 +107,8 @@ def actor(cls, todo: Todo) -> TodoActor: return todo_cls(todo) -def register_all_todos(path=os.path.dirname(__file__), module_path="backend.ticket.todos"): - """递归注册当前目录下所有的todo处理器""" - for name in os.listdir(path): - # 忽略无效文件 - if name.endswith(".pyc") or name in ["__init__.py", "__pycache__"]: - continue - - if os.path.isdir(os.path.join(path, name)): - register_all_todos(os.path.join(path, name), ".".join([module_path, name])) - else: - try: - module_name = name.replace(".py", "") - import_path = ".".join([module_path, module_name]) - importlib.import_module(import_path) - except ModuleNotFoundError as e: - logger.warning(e) +def register_all_todos(): + re_import_modules(path=os.path.dirname(__file__), module_path="backend.ticket.todos") class ActionType(str, StructuredEnum): diff --git a/dbm-ui/backend/urls.py b/dbm-ui/backend/urls.py index 51f2d65164..62d973527d 100644 --- a/dbm-ui/backend/urls.py +++ b/dbm-ui/backend/urls.py @@ -75,6 +75,7 @@ path("grafana/", include("backend.bk_dataview.grafana.urls")), # 版本日志 path("version_log/", include("backend.version_log.urls")), + # 巡检报告 path("db_report/", include("backend.db_report.urls")), # 接入消息通知 path("{}".format(config.ENTRANCE_URL), include("bk_notice_sdk.urls")), diff --git a/dbm-ui/backend/utils/register.py b/dbm-ui/backend/utils/register.py new file mode 100644 index 0000000000..6a9e194f25 --- /dev/null +++ b/dbm-ui/backend/utils/register.py @@ -0,0 +1,33 @@ +# -*- 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. +""" +import importlib +import logging +import os + +logger = logging.getLogger("root") + + +def re_import_modules(path, module_path): + """递归导入文件下的模块,通常用于触发注册器逻辑""" + for name in os.listdir(path): + # 忽略无效文件 + if name.endswith(".pyc") or name in ["__init__.py", "__pycache__"]: + continue + + if os.path.isdir(os.path.join(path, name)): + re_import_modules(os.path.join(path, name), ".".join([module_path, name])) + else: + try: + module_name = name.replace(".py", "") + import_path = ".".join([module_path, module_name]) + importlib.import_module(import_path) + except ModuleNotFoundError as e: + logger.warning(e)