From 8ec14aa7945e660fb63408bb767bb17fe607e91f Mon Sep 17 00:00:00 2001 From: seanlook Date: Wed, 18 Oct 2023 16:50:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(backend):=20mysql=E5=A4=87=E4=BB=BD?= =?UTF-8?q?=E5=B7=A1=E6=A3=80=20close=20#1449?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../local_tasks/mysql_backup/__init__.py | 11 ++ .../local_tasks/mysql_backup/bklog_query.py | 127 +++++++++++++++ .../mysql_backup/check_binlog_backup.py | 95 +++++++++++ .../mysql_backup/check_full_backup.py | 152 ++++++++++++++++++ .../local_tasks/mysql_backup/task.py | 30 ++++ dbm-ui/backend/db_report/enums/__init__.py | 1 + .../enums/mysqlbackup_check_sub_type.py | 18 +++ dbm-ui/backend/db_report/mock_data.py | 15 ++ dbm-ui/backend/db_report/models/__init__.py | 1 + .../models/mysqlbackup_check_report.py | 24 +++ dbm-ui/backend/db_report/urls.py | 2 + dbm-ui/backend/db_report/views/__init__.py | 1 + .../db_report/views/mysqlbackup_check_view.py | 107 ++++++++++++ 13 files changed, 584 insertions(+) create mode 100644 dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/__init__.py create mode 100644 dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/bklog_query.py create mode 100644 dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/check_binlog_backup.py create mode 100644 dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/check_full_backup.py create mode 100644 dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/task.py create mode 100644 dbm-ui/backend/db_report/enums/mysqlbackup_check_sub_type.py create mode 100644 dbm-ui/backend/db_report/models/mysqlbackup_check_report.py create mode 100644 dbm-ui/backend/db_report/views/mysqlbackup_check_view.py diff --git a/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/__init__.py b/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/__init__.py new file mode 100644 index 0000000000..c6116e7fcc --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/__init__.py @@ -0,0 +1,11 @@ +# -*- 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 .task import mysql_backup_check_task diff --git a/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/bklog_query.py b/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/bklog_query.py new file mode 100644 index 0000000000..f39f629afe --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/bklog_query.py @@ -0,0 +1,127 @@ +# -*- 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 datetime +import json +from typing import Dict, List + +from backend import env +from backend.components.bklog.client import BKLogApi +from backend.utils.string import pascal_to_snake + + +def _get_log_from_bklog(collector, start_time, end_time, query_string="*") -> List[Dict]: + """ + 从日志平台获取对应采集项的日志 + @param collector: 采集项名称 + @param start_time: 开始时间 + @param end_time: 结束时间 + @param query_string: 过滤条件 + """ + resp = BKLogApi.esquery_search( + { + "indices": f"{env.DBA_APP_BK_BIZ_ID}_bklog.{collector}", + "start_time": start_time, + "end_time": end_time, + # 这里需要精确查询集群域名,所以可以通过log: "key: \"value\""的格式查询 + "query_string": query_string, + "start": 0, + "size": 6000, + "sort_list": [["dtEventTimeStamp", "asc"], ["gseIndex", "asc"], ["iterationIndex", "asc"]], + }, + use_admin=True, + ) + backup_logs = [] + for hit in resp["hits"]["hits"]: + raw_log = json.loads(hit["_source"]["log"]) + backup_logs.append({pascal_to_snake(key): value for key, value in raw_log.items()}) + + return backup_logs + + +class ClusterBackup: + """ + 集群前一天备份信息,包括全备和binlog + """ + + def __init__(self, cluster_id: int, cluster_domain: str): + self.cluster_id = cluster_id + self.cluster_domain = cluster_domain + self.backups = {} + self.success = False + + def query_backup_log_from_bklog(self, start_time: datetime.datetime, end_time: datetime.datetime) -> List[Dict]: + """ + 通过日志平台查询集群的时间范围内的全备备份记录 + :param start_time: 开始时间 + :param end_time: 结束时间 + """ + backup_files = [] + backup_logs = _get_log_from_bklog( + collector="mysql_backup_result", + start_time=start_time.strftime("%Y-%m-%d %H:%M:%S"), + end_time=end_time.strftime("%Y-%m-%d %H:%M:%S"), + query_string=f'log: "cluster_id: {self.cluster_id}"', + # query_string=f'log: "cluster_address: \\"{self.cluster_domain}\\""', + ) + for log in backup_logs: + bf = { + "backup_id": log["backup_id"], + "cluster_domain": log["cluster_address"], + "cluster_id": log["cluster_id"], + "task_id": log["task_id"], + "file_name": log["file_name"], + "file_size": log["file_size"], + "file_type": log["file_type"], + "mysql_host": log["mysql_host"], + "mysql_port": log["mysql_port"], + "mysql_role": log["mysql_role"], + "backup_type": log["backup_type"], + "data_schema_grant": log["data_schema_grant"], + "backup_begin_time": log["backup_begin_time"], + "backup_end_time": log["backup_end_time"], + "consistent_backup_time": log["consistent_backup_time"], + "shard_value": log["shard_value"], + } + backup_files.append(bf) + return backup_files + + def query_binlog_from_bklog(self, start_time: datetime.datetime, end_time: datetime.datetime) -> List[Dict]: + """ + 通过日志平台查询集群的时间范围内的binlog 备份记录 + :param cluster_domain: 集群 + :param start_time: 开始时间 + :param end_time: 结束时间 + """ + binlogs = [] + backup_logs = _get_log_from_bklog( + collector="mysql_binlog_result", + start_time=start_time.strftime("%Y-%m-%d %H:%M:%S"), + end_time=end_time.strftime("%Y-%m-%d %H:%M:%S"), + query_string=f'log: "cluster_id: {self.cluster_id}"', + # query_string=f'log: "cluster_address: \\"{self.cluster_domain}\\""', + ) + for log in backup_logs: + bl = { + "cluster_domain": log["cluster_domain"], + "cluster_id": log["cluster_id"], + "task_id": log["task_id"], + "file_name": log["filename"], # file_name + "file_size": log["size"], + "file_mtime": log["file_mtime"], + "file_type": "binlog", + "mysql_host": log["host"], + "mysql_port": log["port"], + "mysql_role": log["db_role"], + "backup_status": log["backup_status"], + "backup_status_info": log["backup_status_info"], + } + binlogs.append(bl) + return binlogs diff --git a/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/check_binlog_backup.py b/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/check_binlog_backup.py new file mode 100644 index 0000000000..9e7df18b2a --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/check_binlog_backup.py @@ -0,0 +1,95 @@ +# -*- 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 datetime +from collections import defaultdict + +from django.utils.translation import ugettext as _ + +from backend.db_meta.enums import ClusterType, InstanceInnerRole, InstanceRole, MachineType +from backend.db_meta.models import Cluster +from backend.db_report.enums import MysqlBackupCheckSubType +from backend.db_report.models import MysqlBackupCheckReport + +from .bklog_query import ClusterBackup + + +def check_binlog_backup(): + _check_tendbha_binlog_backup() + _check_tendbcluster_binlog_backup() + + +def _check_tendbha_binlog_backup(): + """ + master 实例必须要有备份binlog + 且binlog序号要连续 + """ + return _check_binlog_backup(ClusterType.TenDBHA) + + +def _check_tendbcluster_binlog_backup(): + """ + master 实例必须要有备份binlog + 且binlog序号要连续 + """ + return _check_binlog_backup(ClusterType.TenDBCluster) + + +def _check_binlog_backup(cluster_type): + """ + master 实例必须要有备份binlog + 且binlog序号要连续 + """ + for c in Cluster.objects.filter(cluster_type=cluster_type): + backup = ClusterBackup(c.id, c.immute_domain) + now_time = datetime.datetime.now().date() + start_time = datetime.datetime.combine(now_time, datetime.time()) + end_time = start_time + datetime.timedelta(hours=23, minutes=59, seconds=59) + items = backup.query_binlog_from_bklog(start_time, end_time) + instance_binlogs = defaultdict(list) + shard_binlog_stat = {} + for i in items: + instance = "{}:{}".format(i.get("mysql_host"), i.get("mysql_port")) + if i.get("mysql_role") == "master" and i.get("backup_status") == 4: + instance_binlogs[instance].append(i.get("file_name")) + backup.success = True + + for inst, binlogs in instance_binlogs.items(): + suffixes = [f.split(".", 1)[1] for f in binlogs] + shard_binlog_stat[inst] = is_consecutive_strings(suffixes) + if not shard_binlog_stat[inst]: + backup.success = False + if not backup.success: + MysqlBackupCheckReport.objects.create( + bk_biz_id=c.bk_biz_id, + bk_cloud_id=c.bk_cloud_id, + cluster=c.immute_domain, + cluster_type=ClusterType.TenDBCluster, + status=False, + msg="binlog is not consecutive:{}".format(shard_binlog_stat), + subtype=MysqlBackupCheckSubType.BinlogSeq.value, + ) + + +def is_consecutive_strings(str_list: list): + """ + 判断字符串数字是否连续 + """ + int_list = [int(s) for s in str_list] + int_sorted = list(set(int_list)) + int_sorted.sort() + if len(int_sorted) == 0: + return False + if len(int_sorted) == 1: + return True + if len(int_sorted) == int_sorted[-1] - int_sorted[0] + 1: + return True + else: + return False diff --git a/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/check_full_backup.py b/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/check_full_backup.py new file mode 100644 index 0000000000..05665acce2 --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/check_full_backup.py @@ -0,0 +1,152 @@ +# -*- 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 datetime +from collections import defaultdict + +from django.utils.translation import ugettext as _ + +from backend.db_meta.enums import ClusterType, InstanceInnerRole, InstanceRole, MachineType +from backend.db_meta.models import Cluster +from backend.db_report.enums import MysqlBackupCheckSubType +from backend.db_report.models import MysqlBackupCheckReport + +from .bklog_query import ClusterBackup + + +def check_full_backup(): + _check_tendbha_full_backup() + _check_tendbcluster_full_backup() + + +class BackupFile: + def __init__(self, file_name: str, file_size: int, file_type=""): + self.file_name = file_name + self.file_size = file_size + self.file_type = file_type + + +class MysqlBackup: + def __init__(self, cluster_id: int, cluster_domain: str, backup_id=""): + self.cluster_id = cluster_id + self.cluster_domain = cluster_domain + self.backup_id = backup_id + self.backup_type = "" + self.backup_role = "" + self.data_schema_grant = "" + self.consistent_backup_time = "" + self.shard_value = -1 + # self.file_index = BackupFile() + # self.file_priv = BackupFile() + self.file_tar = [] + + +def _check_tendbha_full_backup(): + """ + tendbha 必须有一份完整的备份 + """ + for c in Cluster.objects.filter(cluster_type=ClusterType.TenDBHA): + backup = ClusterBackup(c.id, c.immute_domain) + now_time = datetime.datetime.now().date() + start_time = datetime.datetime.combine(now_time, datetime.time()) + end_time = start_time + datetime.timedelta(hours=23, minutes=59, seconds=59) + items = backup.query_backup_log_from_bklog(start_time, end_time) + # print("cluster={} backup_items:{}".format(c.immute_domain, items)) + + for i in items: + if i.get("data_schema_grant", "") == "all": + bid = i.get("backup_id") + if bid not in backup.backups: + backup.backups[bid] = MysqlBackup(i["cluster_id"], i["cluster_domain"], i["backup_id"]) + + bf = BackupFile(i.get("file_name"), i.get("file_size")) + backup.backups[bid].data_schema_grant = i.get("data_schema_grant") + if i.get("file_type") == "index": + backup.backups[bid].file_index = bf + elif i.get("file_type") == "priv": + backup.backups[bid].file_priv = bf + elif i.get("file_type") == "tar": + backup.backups[bid].file_tar.append(bf) + else: + pass + for bid, bk in backup.backups.items(): + if bk.data_schema_grant == "all": + if bk.file_index and bk.file_priv and bk.file_tar: + backup.success = True + break + if not backup.success: + MysqlBackupCheckReport.objects.create( + bk_biz_id=c.bk_biz_id, + bk_cloud_id=c.bk_cloud_id, + cluster=c.immute_domain, + cluster_type=ClusterType.TenDBHA, + status=False, + msg="no success full backup found", + subtype=MysqlBackupCheckSubType.FullBackup.value, + ) + + +def _check_tendbcluster_full_backup(): + """ + tendbcluster 集群必须有完整的备份 + """ + for c in Cluster.objects.filter(cluster_type=ClusterType.TenDBCluster): + backup = ClusterBackup(c.id, c.immute_domain) + now_time = datetime.datetime.now().date() + start_time = datetime.datetime.combine(now_time, datetime.time()) + end_time = start_time + datetime.timedelta(hours=23, minutes=59, seconds=59) + items = backup.query_backup_log_from_bklog(start_time, end_time) + # print("cluster={} backup_items:{}".format(c.immute_domain, items)) + + for i in items: + if i.get("data_schema_grant", "") == "all": + bid = "{}#{}".format(i.get("backup_id"), i.get("shard_value")) + if bid not in backup.backups: + backup.backups[bid] = MysqlBackup(i["cluster_id"], i["cluster_domain"], i["backup_id"]) + + bf = BackupFile(i.get("file_name"), i.get("file_size")) + backup.backups[bid].data_schema_grant = i.get("data_schema_grant") + if i.get("file_type") == "index": + backup.backups[bid].file_index = bf + elif i.get("file_type") == "priv": + backup.backups[bid].file_priv = bf + elif i.get("file_type") == "tar": + backup.backups[bid].file_tar.append(bf) + else: + pass + backup_id_stat = defaultdict(list) + backup_id_invalid = {} + for bid, bk in backup.backups.items(): + backup_id, shard_id = bid.split("#", 1) + if bk.data_schema_grant == "all": + if bk.file_index and bk.file_priv and bk.file_tar: + # 这一个 shard ok + backup_id_stat[backup_id].append({shard_id: True}) + else: + # 这一个 shard 不ok,整个backup_id 无效 + backup_id_invalid[backup_id] = True + backup_id_stat[backup_id].append({shard_id: False}) + message = "" + for backup_id, stat in backup_id_stat.items(): + if backup_id not in backup_id_invalid: + backup.success = True + message = "shard_id:{}".format(backup_id_stat[backup_id]) + break + + if not backup.success: + MysqlBackupCheckReport.objects.create( + bk_biz_id=c.bk_biz_id, + bk_cloud_id=c.bk_cloud_id, + cluster=c.immute_domain, + cluster_type=ClusterType.TenDBCluster, + status=False, + msg="no success full backup found:{}".format(message), + subtype=MysqlBackupCheckSubType.FullBackup.value, + ) diff --git a/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/task.py b/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/task.py new file mode 100644 index 0000000000..24f16b851c --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/local_tasks/mysql_backup/task.py @@ -0,0 +1,30 @@ +# -*- 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 logging + +from celery.schedules import crontab + +from backend.db_periodic_task.local_tasks.register import register_periodic_task + +from .check_binlog_backup import check_binlog_backup +from .check_full_backup import check_full_backup + +logger = logging.getLogger("celery") + + +# @register_periodic_task(run_every=crontab(minute="*/1")) +@register_periodic_task(run_every=crontab(minute=33, hour=2)) +def mysql_backup_check_task(): + """ + mysql 备份巡检 + """ + check_full_backup() + check_binlog_backup() diff --git a/dbm-ui/backend/db_report/enums/__init__.py b/dbm-ui/backend/db_report/enums/__init__.py index ab9d2c95b2..23b6511aab 100644 --- a/dbm-ui/backend/db_report/enums/__init__.py +++ b/dbm-ui/backend/db_report/enums/__init__.py @@ -12,6 +12,7 @@ from django.utils.translation import ugettext_lazy as _ from .meta_check_sub_type import MetaCheckSubType +from .mysqlbackup_check_sub_type import MysqlBackupCheckSubType SWAGGER_TAG = _("巡检报告") diff --git a/dbm-ui/backend/db_report/enums/mysqlbackup_check_sub_type.py b/dbm-ui/backend/db_report/enums/mysqlbackup_check_sub_type.py new file mode 100644 index 0000000000..0648129434 --- /dev/null +++ b/dbm-ui/backend/db_report/enums/mysqlbackup_check_sub_type.py @@ -0,0 +1,18 @@ +# -*- 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 blue_krill.data_types.enum import EnumField, StructuredEnum +from django.utils.translation import gettext_lazy as _ + + +class MysqlBackupCheckSubType(str, StructuredEnum): + FullBackup = EnumField("full_backup", _("集群可用全备")) + BinlogMaster = EnumField("binlog_master", _("主库binlog备份")) + BinlogSeq = EnumField("binlog_seq", _("binlog连续性检查")) diff --git a/dbm-ui/backend/db_report/mock_data.py b/dbm-ui/backend/db_report/mock_data.py index de7a01ed94..c8a49b8958 100644 --- a/dbm-ui/backend/db_report/mock_data.py +++ b/dbm-ui/backend/db_report/mock_data.py @@ -56,3 +56,18 @@ {"name": "msg", "display_name": "失败信息", "format": "text"}, ], } + +MYSQL_BACKUP_CHECK_DATA = { + "count": 1, + "next": None, + "previous": None, + "results": [{"bk_biz_id": 3, "cluster": "aa.bb.cc", "cluster_type": "tendbha", "status": True, "msg": ""}], + "name": "mysql备份检查", + "title": [ + {"name": "bk_biz_id", "display_name": "业务", "format": "text"}, + {"name": "cluster", "display_name": "集群域名", "format": "text"}, + {"name": "cluster_type", "display_name": "集群类型", "format": "text"}, + {"name": "status", "display_name": "元数据状态", "format": "status"}, + {"name": "msg", "display_name": "详情", "format": "text"}, + ], +} diff --git a/dbm-ui/backend/db_report/models/__init__.py b/dbm-ui/backend/db_report/models/__init__.py index 5ff4ab7f58..f189bb3f0b 100644 --- a/dbm-ui/backend/db_report/models/__init__.py +++ b/dbm-ui/backend/db_report/models/__init__.py @@ -10,3 +10,4 @@ """ from .checksum_check_report import ChecksumCheckReport, ChecksumInstance from .meta_check_report import MetaCheckReport +from .mysqlbackup_check_report import MysqlBackupCheckReport diff --git a/dbm-ui/backend/db_report/models/mysqlbackup_check_report.py b/dbm-ui/backend/db_report/models/mysqlbackup_check_report.py new file mode 100644 index 0000000000..e1c7de5e99 --- /dev/null +++ b/dbm-ui/backend/db_report/models/mysqlbackup_check_report.py @@ -0,0 +1,24 @@ +# -*- 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.db import models +from django.utils.translation import ugettext_lazy as _ + +from backend.db_meta.enums import ClusterType +from backend.db_report.enums import MysqlBackupCheckSubType +from backend.db_report.report_basemodel import BaseReportABS + + +class MysqlBackupCheckReport(BaseReportABS): + cluster = models.CharField(max_length=255, default="") + cluster_type = models.CharField(max_length=64, choices=ClusterType.get_choices(), default="") + subtype = models.CharField( + max_length=64, choices=MysqlBackupCheckSubType.get_choices(), default="", help_text=_("备份检查子项") + ) diff --git a/dbm-ui/backend/db_report/urls.py b/dbm-ui/backend/db_report/urls.py index a6957d85c8..c60d9e751e 100644 --- a/dbm-ui/backend/db_report/urls.py +++ b/dbm-ui/backend/db_report/urls.py @@ -16,4 +16,6 @@ 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"})), ] diff --git a/dbm-ui/backend/db_report/views/__init__.py b/dbm-ui/backend/db_report/views/__init__.py index 3edf985448..39ec7ab22d 100644 --- a/dbm-ui/backend/db_report/views/__init__.py +++ b/dbm-ui/backend/db_report/views/__init__.py @@ -11,3 +11,4 @@ from .checksum_check_report_view import ChecksumCheckReportViewSet from .checksum_instance_view import ChecksumInstanceViewSet from .meta_check_view import MetaCheckReportInstanceBelongViewSet +from .mysqlbackup_check_view import MysqlBinlogBackupCheckReportViewSet, MysqlFullBackupCheckReportViewSet diff --git a/dbm-ui/backend/db_report/views/mysqlbackup_check_view.py b/dbm-ui/backend/db_report/views/mysqlbackup_check_view.py new file mode 100644 index 0000000000..2223bf0839 --- /dev/null +++ b/dbm-ui/backend/db_report/views/mysqlbackup_check_view.py @@ -0,0 +1,107 @@ +# -*- 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 logging + +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers, status + +from backend.bk_web.swagger import common_swagger_auto_schema +from backend.db_report import mock_data +from backend.db_report.enums import SWAGGER_TAG, MysqlBackupCheckSubType, ReportFieldFormat +from backend.db_report.models import MysqlBackupCheckReport +from backend.db_report.report_baseview import ReportBaseViewSet + +logger = logging.getLogger("root") + + +class MysqlBackupCheckReportSerializer(serializers.ModelSerializer): + class Meta: + model = MysqlBackupCheckReport + fields = ("bk_biz_id", "cluster", "cluster_type", "status", "msg") + swagger_schema_fields = {"example": mock_data.MYSQL_BACKUP_CHECK_DATA} + + +class MysqlBackupCheckReportBaseViewSet(ReportBaseViewSet): + queryset = MysqlBackupCheckReport.objects.all() + serializer_class = MysqlBackupCheckReportSerializer + filter_fields = { # 大部分时候不需要覆盖默认的filter + "bk_biz_id": ["exact"], + "cluster_type": ["exact", "in"], + "create_at": ["gte", "lte"], + "status": ["exact", "in"], + } + report_name = _("集群全备检查") + report_title = [ + { + "name": "bk_biz_id", + "display_name": _("业务"), + "format": ReportFieldFormat.TEXT.value, + }, + { + "name": "cluster", + "display_name": _("集群域名"), + "format": ReportFieldFormat.TEXT.value, + }, + { + "name": "cluster_type", + "display_name": _("集群类型"), + "format": ReportFieldFormat.TEXT.value, + }, + { + "name": "status", + "display_name": _("全备状态"), + "format": ReportFieldFormat.STATUS.value, + }, + { + "name": "msg", + "display_name": _("详情"), + "format": ReportFieldFormat.TEXT.value, + }, + ] + + @common_swagger_auto_schema( + operation_summary=_("备份检查报告"), + responses={status.HTTP_200_OK: MysqlBackupCheckReportSerializer()}, + tags=[SWAGGER_TAG], + ) + def list(self, request, *args, **kwargs): + logger.info("list") + return super().list(request, *args, **kwargs) + + +class MysqlFullBackupCheckReportViewSet(MysqlBackupCheckReportBaseViewSet): + queryset = MysqlBackupCheckReport.objects.filter(subtype=MysqlBackupCheckSubType.FullBackup.value) + serializer_class = MysqlBackupCheckReportSerializer + report_name = _("全备检查") + + @common_swagger_auto_schema( + operation_summary=_("全备检查报告"), + responses={status.HTTP_200_OK: MysqlBackupCheckReportSerializer()}, + tags=[SWAGGER_TAG], + ) + def list(self, request, *args, **kwargs): + return super().list(request, *args, **kwargs) + + +class MysqlBinlogBackupCheckReportViewSet(MysqlBackupCheckReportBaseViewSet): + queryset = MysqlBackupCheckReport.objects.filter(subtype=MysqlBackupCheckSubType.BinlogSeq.value) + serializer_class = MysqlBackupCheckReportSerializer + report_name = _("集群binlog检查") + + @common_swagger_auto_schema( + operation_summary=_("binlog检查报告"), + responses={status.HTTP_200_OK: MysqlBackupCheckReportSerializer()}, + tags=[SWAGGER_TAG], + ) + def list(self, request, *args, **kwargs): + return super().list(request, *args, **kwargs)