Skip to content

Commit be813b1

Browse files
committed
feat(backend): dba工具箱-实例级别主库故障切换 #9538
# Reviewed, transaction id: 36770
1 parent 4fcb236 commit be813b1

File tree

14 files changed

+453
-28
lines changed

14 files changed

+453
-28
lines changed

dbm-ui/backend/db_meta/api/cluster/nosqlcomm/create_cluster.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from backend.db_meta.api import common
2222
from backend.db_meta.enums import AccessLayer, ClusterEntryType, ClusterPhase, ClusterStatus, ClusterType, MachineType
2323
from backend.db_meta.models import Cluster, ClusterEntry, ProxyInstance, StorageInstance
24-
from backend.db_services.dbbase.constants import IP_PORT_DIVIDER
2524
from backend.flow.utils.redis.redis_module_operate import RedisCCTopoOperator
2625

2726
from ....exceptions import (
@@ -59,6 +58,7 @@ def create_twemproxy_cluster(
5958
2. proxy 不能有已绑定的后端
6059
3. 必须只有 1 个 master
6160
"""
61+
from backend.db_services.dbbase.constants import IP_PORT_DIVIDER
6262

6363
ip_port_storages = {"{}{}{}".format(s["ip"], IP_PORT_DIVIDER, s["port"]): s for s in storages}
6464

dbm-ui/backend/db_services/bigdata/influxdb/query.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from backend.db_meta.models.group import Group, GroupInstance
1919
from backend.db_meta.models.instance import StorageInstance
2020
from backend.db_services.bigdata.resources.query import BigDataBaseListRetrieveResource
21-
from backend.db_services.dbbase.constants import IP_PORT_DIVIDER
2221
from backend.db_services.dbbase.resources import query
2322
from backend.db_services.dbbase.resources.register import register_resource_decorator
2423
from backend.db_services.ipchooser.query.resource import ResourceQueryHelper
@@ -129,6 +128,7 @@ def _to_instance(
129128
cls, instance: dict, restart_map: dict, group_id_map: dict, group_name_map: dict, host_id__host_map: dict
130129
) -> dict:
131130
"""实例序列化"""
131+
from backend.db_services.dbbase.constants import IP_PORT_DIVIDER
132132

133133
instance_id = instance["id"]
134134
restart_at = restart_map.get(instance_id, "")

dbm-ui/backend/db_services/cmdb/biz.py

+86-1
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
import logging
1313
from typing import Dict, List
1414

15+
from django.db.models import Count
16+
1517
from backend.components import CCApi
1618
from backend.components.dbconfig.constants import DEPLOY_FILE_NAME, ConfType, LevelName
17-
from backend.db_meta.models import AppCache, DBModule
19+
from backend.db_meta.models import AppCache, Cluster, DBModule, Machine, ProxyInstance, StorageInstance
20+
from backend.db_services.cmdb.constants import ModuleCountType
1821
from backend.db_services.cmdb.exceptions import BkAppAttrAlreadyExistException
1922
from backend.db_services.dbconfig.dataclass import DBBaseConfig, DBConfigLevelData
2023
from backend.db_services.dbconfig.handlers import DBConfigHandler
@@ -222,3 +225,85 @@ def get_or_create_set_with_name(bk_biz_id: int, bk_set_name: str) -> int:
222225
raise err
223226
else:
224227
return bk_set_id
228+
229+
230+
def filter_by_biz_name(data: list, biz_name: str) -> list:
231+
return [biz for biz in data if biz["bk_biz_name"] == biz_name]
232+
233+
234+
def filter_by_module_name(data: list, module_name: str) -> list:
235+
result = []
236+
for biz in data:
237+
filtered_modules = [module for module in biz["modules"] if module["module_name"] == module_name]
238+
if filtered_modules:
239+
new_biz = biz.copy()
240+
new_biz["modules"] = filtered_modules
241+
result.append(new_biz)
242+
return result
243+
244+
245+
def process_count_obj(queryset, nested_data):
246+
count_obj = queryset.values("db_module_id", "bk_biz_id").annotate(count=Count("db_module_id")).order_by("-count")
247+
for obj in count_obj:
248+
bk_biz_id = obj["bk_biz_id"]
249+
db_module_id = obj["db_module_id"]
250+
count = obj["count"]
251+
nested_data[bk_biz_id]["count"] += count
252+
nested_data[bk_biz_id]["modules"][db_module_id] = count
253+
254+
255+
def list_biz_module_trees(
256+
cluster_types: str, bk_biz_name: str, module_name: str, count_type: str, instance_inner_role: str
257+
) -> List[Dict]:
258+
"""
259+
获取业务与模块维度集群数量
260+
:param cluster_types: 以逗号分隔的集群类型字符串
261+
:param bk_biz_name: 业务名称
262+
:param module_name: 模块名称
263+
:param count_type: 计数类型(如 "cluster" 或 "instance")
264+
:param instance_inner_role: 是否过滤为后端主实例
265+
:return: 包含业务和模块维度的计数列表
266+
"""
267+
cluster_type_list = cluster_types.split(",")
268+
269+
if count_type == ModuleCountType.CLUSTER.value:
270+
queryset = Cluster.objects.filter(cluster_type__in=cluster_type_list)
271+
elif count_type == ModuleCountType.INSTANCE.value:
272+
filters = {"cluster_type__in": cluster_type_list}
273+
if instance_inner_role:
274+
filters["instance_inner_role"] = instance_inner_role
275+
queryset = StorageInstance.objects.filter(**filters)
276+
elif count_type == ModuleCountType.MACHINE.value:
277+
queryset = Machine.objects.filter(cluster_type__in=cluster_type_list)
278+
else:
279+
raise ValueError(f"Unsupported count_type: {count_type}")
280+
281+
nested_data = collections.defaultdict(lambda: {"count": 0, "modules": collections.defaultdict(int)})
282+
process_count_obj(queryset, nested_data)
283+
284+
if count_type == ModuleCountType.INSTANCE.value:
285+
proxy_queryset = ProxyInstance.objects.filter(cluster_type__in=cluster_type_list)
286+
process_count_obj(proxy_queryset, nested_data)
287+
288+
db_module_map = DBModule.db_module_map()
289+
id_to_name = AppCache.id_to_name()
290+
291+
final_data = [
292+
{
293+
"bk_biz_name": id_to_name.get(bk_biz_id, ""),
294+
"bk_biz_id": bk_biz_id,
295+
"count": data["count"],
296+
"modules": [
297+
{"module_name": db_module_map.get(module_id, ""), "module_id": module_id, "count": count}
298+
for module_id, count in data["modules"].items()
299+
],
300+
}
301+
for bk_biz_id, data in nested_data.items()
302+
]
303+
304+
if bk_biz_name:
305+
final_data = filter_by_biz_name(final_data, bk_biz_name)
306+
if module_name:
307+
final_data = filter_by_module_name(final_data, module_name)
308+
309+
return final_data

dbm-ui/backend/db_services/cmdb/constants.py

+11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@
88
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
99
specific language governing permissions and limitations under the License.
1010
"""
11+
from django.utils.translation import gettext as _
12+
13+
from blue_krill.data_types.enum import EnumField, StructuredEnum
1114

1215
MAX_DB_MODULE_LIMIT = 63
1316
MAX_DB_APP_ABBR_LIMIT = 63
17+
18+
19+
class ModuleCountType(str, StructuredEnum):
20+
"""module统计类型"""
21+
22+
CLUSTER = EnumField("cluster", _("集群"))
23+
INSTANCE = EnumField("instance", _("实例"))
24+
MACHINE = EnumField("machine", _("主机"))

dbm-ui/backend/db_services/cmdb/serializers.py

+24-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
from django.utils.translation import gettext_lazy as _
1212
from rest_framework import serializers
1313

14-
from backend.db_meta.enums import ClusterType
15-
from backend.db_services.cmdb.constants import MAX_DB_APP_ABBR_LIMIT, MAX_DB_MODULE_LIMIT
14+
from backend.db_meta.enums import ClusterType, InstanceInnerRole
15+
from backend.db_services.cmdb.constants import MAX_DB_APP_ABBR_LIMIT, MAX_DB_MODULE_LIMIT, ModuleCountType
1616
from backend.iam_app.dataclass import ResourceEnum
1717
from backend.iam_app.dataclass.actions import ActionEnum
1818

@@ -80,3 +80,25 @@ class ListNodesSerializer(TopoSerializer):
8080
page = serializers.IntegerField(help_text=_("页数"))
8181
module_id = serializers.IntegerField(help_text=_("模块ID"), required=False)
8282
set_id = serializers.IntegerField(help_text=_("集群ID"), required=False)
83+
84+
85+
class ListBIZModulesSLZ(serializers.Serializer):
86+
cluster_types = serializers.CharField(help_text=_("集群类型(逗号分隔)"))
87+
bk_biz_name = serializers.CharField(help_text=_("业务名称"), required=False)
88+
module_name = serializers.CharField(help_text=_("模块名称"), required=False)
89+
count_type = serializers.ChoiceField(choices=ModuleCountType.get_choices(), default=ModuleCountType.CLUSTER.value)
90+
instance_inner_role = serializers.ChoiceField(
91+
help_text=_("实例角色"), choices=InstanceInnerRole.get_choices(), required=False
92+
)
93+
94+
95+
class BIZModuleSLZ(serializers.Serializer):
96+
class ModuleInstanceCountSLZ(serializers.Serializer):
97+
module_name = serializers.CharField(help_text=_("模块名"))
98+
module_id = serializers.IntegerField(help_text=_("模块ID"))
99+
count = serializers.IntegerField(help_text=_("实例数量"))
100+
101+
bk_biz_name = serializers.CharField(help_text=_("业务名"))
102+
bk_biz_id = serializers.IntegerField(help_text=_("业务ID"))
103+
count = serializers.IntegerField(help_text=_("实例数量"))
104+
modules = serializers.ListField(help_text=_("模块信息"), child=ModuleInstanceCountSLZ())

dbm-ui/backend/db_services/cmdb/views.py

+20
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,23 @@ def set_db_app_abbr(self, request, bk_biz_id):
9999
@action(methods=["GET"], detail=True)
100100
def list_cc_obj_user(self, request, bk_biz_id):
101101
return Response(biz.list_cc_obj_user(bk_biz_id))
102+
103+
@common_swagger_auto_schema(
104+
operation_summary=_("业务模块树信息"),
105+
query_serializer=serializers.ListBIZModulesSLZ(),
106+
responses={status.HTTP_200_OK: serializers.BIZModuleSLZ(label=_("业务模块树信息"), many=True)},
107+
tags=[SWAGGER_TAG],
108+
)
109+
@action(methods=["GET"], detail=False, serializer_class=serializers.ListBIZModulesSLZ)
110+
def list_biz_module_trees(self, request):
111+
validated_data = self.params_validate(self.get_serializer_class())
112+
cluster_types = validated_data.get("cluster_types")
113+
bk_biz_name = validated_data.get("bk_biz_name")
114+
module_name = validated_data.get("module_name")
115+
count_type = validated_data.get("count_type")
116+
instance_inner_role = validated_data.get("instance_inner_role")
117+
serializer = serializers.BIZModuleSLZ(
118+
biz.list_biz_module_trees(cluster_types, bk_biz_name, module_name, count_type, instance_inner_role),
119+
many=True,
120+
)
121+
return Response(serializer.data)

dbm-ui/backend/db_services/dbbase/cluster/handlers.py

+23
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from django.forms import model_to_dict
1919
from django.utils.translation import ugettext_lazy as _
2020

21+
from backend.configuration.constants import DBType
2122
from backend.db_meta.enums import AccessLayer, ClusterType, InstanceInnerRole, InstanceStatus
2223
from backend.db_meta.exceptions import ClusterNotExistException, InstanceNotExistException
2324
from backend.db_meta.models import Cluster, ProxyInstance, StorageInstance, StorageInstanceTuple
@@ -368,3 +369,25 @@ def get_cluster_service_handler(bk_biz_id: int, db_type: str = "dbbase"):
368369
handler = ClusterServiceHandler(bk_biz_id)
369370

370371
return handler
372+
373+
374+
def retrieve_resources(self, request, serializer_class, resource_method_name):
375+
"""
376+
通用方法来处理不同资源类型的请求。
377+
"""
378+
from backend.db_services.dbbase.constants import DB_RESOURCE_MAP
379+
380+
query_params = self.params_validate(serializer_class)
381+
bk_biz_id = query_params.pop("bk_biz_id", None)
382+
db_type = query_params.pop("db_type", None)
383+
RetrieveResource = DB_RESOURCE_MAP[db_type]
384+
385+
# 为 MySQL 和 SQLServer 设置集群类型
386+
if db_type in [DBType.MySQL.value, DBType.Sqlserver.value]:
387+
RetrieveResource.cluster_types = ClusterType.db_type_cluster_types_map().get(db_type)
388+
389+
# 动态调用资源方法获取数据
390+
resource_method = getattr(RetrieveResource, resource_method_name)
391+
data = self.paginator.paginate_list(request, bk_biz_id, resource_method, query_params)
392+
393+
return self.get_paginated_response(data)

dbm-ui/backend/db_services/dbbase/constants.py

+33
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,22 @@
1010
"""
1111
from django.utils.translation import ugettext_lazy as _
1212

13+
from backend.configuration.constants import DBType
14+
from backend.db_services.bigdata.doris.query import DorisListRetrieveResource
15+
from backend.db_services.bigdata.es.query import ESListRetrieveResource
16+
from backend.db_services.bigdata.hdfs.query import HDFSListRetrieveResource
17+
from backend.db_services.bigdata.influxdb.query import InfluxDBListRetrieveResource
18+
from backend.db_services.bigdata.kafka.query import KafkaListRetrieveResource
19+
from backend.db_services.bigdata.pulsar.query import PulsarListRetrieveResource
20+
from backend.db_services.bigdata.riak.query import RiakListRetrieveResource
21+
from backend.db_services.bigdata.vm.query import VMListRetrieveResource
22+
from backend.db_services.mongodb.resources.query import MongoDBListRetrieveResource
23+
from backend.db_services.mysql.resources.tendbcluster.query import (
24+
ListRetrieveResource as TendDBClusterRetrieveResource,
25+
)
26+
from backend.db_services.mysql.resources.tendbha.query import ListRetrieveResource as TendDBHAHaRetrieveResource
27+
from backend.db_services.redis.resources.redis_cluster.query import RedisListRetrieveResource
28+
from backend.db_services.sqlserver.resources.query import SqlserverListRetrieveResource
1329
from blue_krill.data_types.enum import EnumField, StructuredEnum
1430

1531
ES_DEFAULT_PORT = 9200
@@ -44,3 +60,20 @@ class ResourceType(str, StructuredEnum):
4460

4561
SPOTTY_HOST = EnumField("spotty_host", _("污点主机"))
4662
RESOURCE_RECORD = EnumField("resource_record", _("资源操作记录"))
63+
64+
65+
DB_RESOURCE_MAP = {
66+
DBType.TenDBCluster.value: TendDBHAHaRetrieveResource,
67+
DBType.MySQL.value: TendDBClusterRetrieveResource,
68+
DBType.Redis.value: RedisListRetrieveResource,
69+
DBType.MongoDB.value: MongoDBListRetrieveResource,
70+
DBType.Kafka.value: KafkaListRetrieveResource,
71+
DBType.Hdfs.value: HDFSListRetrieveResource,
72+
DBType.Es.value: ESListRetrieveResource,
73+
DBType.Pulsar.value: PulsarListRetrieveResource,
74+
DBType.InfluxDB.value: InfluxDBListRetrieveResource,
75+
DBType.Riak.value: RiakListRetrieveResource,
76+
DBType.Sqlserver.value: SqlserverListRetrieveResource,
77+
DBType.Doris.value: DorisListRetrieveResource,
78+
DBType.Vm.value: VMListRetrieveResource,
79+
}

0 commit comments

Comments
 (0)