Skip to content

Commit 78ddfc2

Browse files
committed
feat(backend): 集群标准化需求新增业务模块导航与mysql单节点、主从集群列表汇总接口 #6628
# Reviewed, transaction id: 24824
1 parent 6e6d856 commit 78ddfc2

File tree

13 files changed

+246
-21
lines changed

13 files changed

+246
-21
lines changed

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

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
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
1820
from backend.db_services.cmdb.exceptions import BkAppAttrAlreadyExistException
1921
from backend.db_services.dbconfig.dataclass import DBBaseConfig, DBConfigLevelData
2022
from backend.db_services.dbconfig.handlers import DBConfigHandler
@@ -221,3 +223,63 @@ def get_or_create_set_with_name(bk_biz_id: int, bk_set_name: str) -> int:
221223
raise err
222224
else:
223225
return bk_set_id
226+
227+
228+
def filter_by_biz_name(data: list, biz_name: str) -> list:
229+
return [biz for biz in data if biz["bk_biz_name"] == biz_name]
230+
231+
232+
def filter_by_module_name(data: list, module_name: str) -> list:
233+
result = []
234+
for biz in data:
235+
filtered_modules = [module for module in biz["modules"] if module["module_name"] == module_name]
236+
if filtered_modules:
237+
new_biz = biz.copy()
238+
new_biz["modules"] = filtered_modules
239+
result.append(new_biz)
240+
return result
241+
242+
243+
def list_biz_module_trees(cluster_types: str, bk_biz_name: str, module_name: str) -> List[Dict]:
244+
"""
245+
获取业务与模块维度集群数量
246+
"""
247+
248+
clusters = (
249+
Cluster.objects.filter(cluster_type__in=cluster_types.split(","))
250+
.values("db_module_id", "bk_biz_id")
251+
.annotate(count=Count("db_module_id"))
252+
.order_by("-count")
253+
)
254+
255+
db_module_map = DBModule.db_module_map()
256+
id_to_name = AppCache.id_to_name()
257+
258+
nested_data = collections.defaultdict(lambda: {"count": 0, "modules": collections.defaultdict(int)})
259+
for cluster in clusters:
260+
bk_biz_id = cluster["bk_biz_id"]
261+
db_module_id = cluster["db_module_id"]
262+
count = cluster["count"]
263+
nested_data[bk_biz_id]["count"] += count
264+
nested_data[bk_biz_id]["modules"][db_module_id] = count
265+
266+
final_data = []
267+
for bk_biz_id, data in nested_data.items():
268+
modules = [
269+
{"module_name": db_module_map.get(module_id), "module_id": module_id, "count": count}
270+
for module_id, count in data["modules"].items()
271+
]
272+
final_data.append(
273+
{
274+
"bk_biz_name": id_to_name.get(bk_biz_id),
275+
"bk_biz_id": bk_biz_id,
276+
"count": data["count"],
277+
"modules": modules,
278+
}
279+
)
280+
281+
if bk_biz_name:
282+
final_data = filter_by_biz_name(final_data, bk_biz_name)
283+
if module_name:
284+
final_data = filter_by_module_name(final_data, module_name)
285+
return final_data

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,21 @@ class ListNodesSerializer(TopoSerializer):
7979
page = serializers.IntegerField(help_text=_("页数"))
8080
module_id = serializers.IntegerField(help_text=_("模块ID"), required=False)
8181
set_id = serializers.IntegerField(help_text=_("集群ID"), required=False)
82+
83+
84+
class ListBIZModulesSLZ(serializers.Serializer):
85+
cluster_types = serializers.CharField(help_text=_("集群类型(逗号分隔)"))
86+
bk_biz_name = serializers.CharField(help_text=_("业务名称"), required=False)
87+
module_name = serializers.CharField(help_text=_("模块名称"), required=False)
88+
89+
90+
class BIZModuleSLZ(serializers.Serializer):
91+
class ModuleClusterCountSLZ(serializers.Serializer):
92+
module_name = serializers.CharField(help_text=_("模块名"))
93+
module_id = serializers.IntegerField(help_text=_("模块ID"))
94+
count = serializers.IntegerField(help_text=_("集群数量"))
95+
96+
bk_biz_name = serializers.CharField(help_text=_("业务名"))
97+
bk_biz_id = serializers.IntegerField(help_text=_("业务ID"))
98+
count = serializers.IntegerField(help_text=_("集群数量"))
99+
modules = serializers.ListField(help_text=_("模块信息"), child=ModuleClusterCountSLZ())

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
urlpatterns = [
1717
path("bizs/", CMDBViewSet.as_view({"get": "list_bizs"})),
1818
path("bizs/<int:bk_biz_id>/modules/", CMDBViewSet.as_view({"get": "list_modules"})),
19+
path("biz_module_trees/", CMDBViewSet.as_view({"get": "list_biz_module_trees"})),
1920
]
2021

2122
routers = DefaultRouter(trailing_slash=True)

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,19 @@ def set_db_app_abbr(self, request, bk_biz_id):
9898
@action(methods=["GET"], detail=True)
9999
def list_cc_obj_user(self, request, bk_biz_id):
100100
return Response(biz.list_cc_obj_user(bk_biz_id))
101+
102+
@common_swagger_auto_schema(
103+
operation_summary=_("业务模块树信息"),
104+
query_serializer=serializers.ListBIZModulesSLZ(),
105+
responses={status.HTTP_200_OK: serializers.BIZModuleSLZ(label=_("业务模块树信息"), many=True)},
106+
tags=[SWAGGER_TAG],
107+
)
108+
@action(methods=["GET"], detail=False, serializer_class=serializers.ListBIZModulesSLZ)
109+
def list_biz_module_trees(self, request):
110+
cluster_types = self.params_validate(self.get_serializer_class()).get("cluster_types")
111+
bk_biz_name = self.params_validate(self.get_serializer_class()).get("bk_biz_name")
112+
module_name = self.params_validate(self.get_serializer_class()).get("module_name")
113+
serializer = serializers.BIZModuleSLZ(
114+
biz.list_biz_module_trees(cluster_types, bk_biz_name, module_name), many=True
115+
)
116+
return Response(serializer.data)

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

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
from backend.configuration.constants import DBType
1818
from backend.db_dirty.models import DirtyMachine
1919
from backend.db_meta.enums import ClusterPhase, ClusterType
20+
from backend.db_meta.models import DBModule
2021
from backend.db_services.dbbase.constants import ResourceType
22+
from backend.db_services.dbbase.resources.query_base import build_q_for_domain_by_cluster
2123
from backend.db_services.dbbase.resources.serializers import ListResourceSLZ
2224
from backend.db_services.ipchooser.query.resource import ResourceQueryHelper
2325
from backend.db_services.redis.resources.redis_cluster.query import RedisListRetrieveResource
@@ -55,6 +57,83 @@ def get_conditions(self, attr):
5557
return conditions
5658

5759

60+
class QueryAllTypeClusterSerializerV2(serializers.Serializer):
61+
bk_biz_id = serializers.IntegerField(help_text=_("业务ID"), required=False)
62+
cluster_types = serializers.CharField(help_text=_("集群类型(逗号分隔)"), required=False)
63+
immute_domain = serializers.CharField(help_text=_("集群域名"), required=False)
64+
# 额外过滤参数
65+
phase = serializers.ChoiceField(help_text=_("集群阶段状态"), required=False, choices=ClusterPhase.get_choices())
66+
name = serializers.CharField(help_text=_("集群英文名"), required=False)
67+
alias = serializers.CharField(help_text=_("集群别名"), required=False)
68+
db_module_id = serializers.IntegerField(help_text=_("模块id"), required=False)
69+
major_version = serializers.CharField(help_text=_("主版本号"), required=False)
70+
status = serializers.CharField(help_text=_("状态"), required=False)
71+
bk_cloud_id = serializers.IntegerField(help_text=_("云区域 ID"), required=False)
72+
region = serializers.CharField(help_text=_("地域"), required=False)
73+
db_module_name = serializers.CharField(help_text=_("模块名"), required=False)
74+
cluster_type = serializers.CharField(help_text=_("集群类型"), required=False)
75+
id = serializers.IntegerField(help_text=_("集群ID"), required=False)
76+
77+
_db_module_id_name_map = None
78+
_db_module_name_id_map = None
79+
_cloud_info = None
80+
81+
@property
82+
def db_module_id_name_map(self):
83+
if self._db_module_id_name_map is None:
84+
self._db_module_id_name_map = DBModule.db_module_map()
85+
return self._db_module_id_name_map
86+
87+
@property
88+
def db_module_name_id_map(self):
89+
if self._db_module_name_id_map is None:
90+
self._db_module_name_id_map = {
91+
module_name: module_id for module_id, module_name in self.db_module_id_name_map
92+
}
93+
return self._db_module_name_id_map
94+
95+
@property
96+
def cloud_info(self):
97+
if self._cloud_info is None:
98+
self._cloud_info = ResourceQueryHelper.search_cc_cloud(get_cache=True)
99+
return self._cloud_info
100+
101+
def get_conditions(self, attr):
102+
conditions = Q()
103+
104+
if attr.get("cluster_types"):
105+
conditions &= Q(cluster_type__in=attr["cluster_types"].split(","))
106+
attr.pop("cluster_types")
107+
108+
if attr.get("db_module_name"):
109+
db_module_id = self.db_module_name_id_map.get(attr["db_module_name"])
110+
if db_module_id is not None:
111+
conditions &= Q(db_module_id=db_module_id)
112+
attr.pop("db_module_name")
113+
114+
if attr.get("immute_domain"):
115+
# 支持从域名查询
116+
conditions &= build_q_for_domain_by_cluster(domains=attr.get("immute_domain", "").split(","))
117+
attr.pop("immute_domain")
118+
119+
for field in self.fields.keys():
120+
if field in attr:
121+
conditions &= Q(**{field: attr[field]})
122+
123+
return conditions
124+
125+
def to_representation(self, instance):
126+
representation = super().to_representation(instance)
127+
db_module_id = representation.get("db_module_id")
128+
representation["db_module_name"] = self.db_module_id_name_map.get(db_module_id, "")
129+
try:
130+
representation["bk_cloud_name"] = self.cloud_info[str(representation["bk_cloud_id"])]["bk_cloud_name"]
131+
except Exception:
132+
representation["bk_cloud_name"] = ""
133+
134+
return representation
135+
136+
58137
class QueryAllTypeClusterResponseSerializer(serializers.Serializer):
59138
class Meta:
60139
swagger_schema_fields = {"example": [{"id": 47, "immute_domain": "mysql.dba.db.com"}]}

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,15 @@
3838
IsClusterDuplicatedSerializer,
3939
QueryAllTypeClusterResponseSerializer,
4040
QueryAllTypeClusterSerializer,
41+
QueryAllTypeClusterSerializerV2,
4142
QueryBizClusterAttrsResponseSerializer,
4243
QueryBizClusterAttrsSerializer,
4344
ResourceAdministrationSerializer,
4445
WebConsoleResponseSerializer,
4546
WebConsoleSerializer,
4647
)
4748
from backend.db_services.ipchooser.query.resource import ResourceQueryHelper
48-
from backend.iam_app.handlers.drf_perm.base import DBManagePermission
49+
from backend.iam_app.handlers.drf_perm.base import DBManagePermission, ListResourcePermission
4950
from backend.iam_app.handlers.drf_perm.cluster import ClusterWebconsolePermission
5051

5152
SWAGGER_TAG = _("集群通用接口")
@@ -60,11 +61,9 @@ class DBBaseViewSet(viewsets.SystemViewSet):
6061

6162
action_permission_map = {
6263
("verify_duplicated_cluster_name",): [],
63-
(
64-
"simple_query_cluster",
65-
"common_query_cluster",
66-
): [DBManagePermission()],
64+
("common_query_cluster",): [DBManagePermission()],
6765
("webconsole",): [ClusterWebconsolePermission()],
66+
("simple_query_cluster", "simple_query_cluster_v2"): [ListResourcePermission()],
6867
}
6968
default_permission_class = [DBManagePermission()]
7069

@@ -96,6 +95,27 @@ def simple_query_cluster(self, request, *args, **kwargs):
9695
cluster_infos = [cluster.simple_desc for cluster in cluster_queryset]
9796
return Response(cluster_infos)
9897

98+
@common_swagger_auto_schema(
99+
operation_summary=_("查询业务集群简略信息"),
100+
auto_schema=ResponseSwaggerAutoSchema,
101+
query_serializer=QueryAllTypeClusterSerializerV2(),
102+
responses={status.HTTP_200_OK: QueryAllTypeClusterResponseSerializer()},
103+
tags=[SWAGGER_TAG],
104+
)
105+
@action(methods=["GET"], detail=False, serializer_class=QueryAllTypeClusterSerializerV2)
106+
def simple_query_cluster_v2(self, request, *args, **kwargs):
107+
data = self.params_validate(self.get_serializer_class())
108+
conditions = self.get_serializer().get_conditions(data)
109+
cluster_queryset = Cluster.objects.filter(conditions)
110+
111+
page = self.paginate_queryset(cluster_queryset)
112+
if page is not None:
113+
serializer = self.get_serializer(page, many=True)
114+
return self.get_paginated_response(serializer.data)
115+
116+
serializer = self.get_serializer(cluster_queryset, many=True)
117+
return Response(serializer.data)
118+
99119
@common_swagger_auto_schema(
100120
operation_summary=_("查询业务下集群通用信息"),
101121
auto_schema=ResponseSwaggerAutoSchema,

dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_standardize_flow.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,11 @@ def standardize(self):
6363
增加单据临时ADMIN账号的添加和删除逻辑
6464
"""
6565
cluster_ids = self.data["infos"]["cluster_ids"]
66-
bk_biz_id = self.data["bk_biz_id"]
66+
# 支持跨业务
67+
# bk_biz_id = self.data["bk_biz_id"]
6768

6869
cluster_objects = Cluster.objects.filter(
69-
pk__in=cluster_ids, bk_biz_id=bk_biz_id, cluster_type=ClusterType.TenDBHA.value
70+
pk__in=cluster_ids, cluster_type=ClusterType.TenDBHA.value
7071
).prefetch_related(
7172
"proxyinstance_set", "storageinstance_set", "proxyinstance_set__machine", "storageinstance_set__machine"
7273
)

dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_standardize_flow.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,11 @@ def standardize(self):
6161
}
6262
"""
6363
cluster_ids = self.data["infos"]["cluster_ids"]
64-
bk_biz_id = self.data["bk_biz_id"]
64+
# 支持跨业务
65+
# bk_biz_id = self.data["bk_biz_id"]
6566

6667
cluster_objects = Cluster.objects.filter(
67-
pk__in=cluster_ids, bk_biz_id=bk_biz_id, cluster_type=ClusterType.TenDBCluster.value
68+
pk__in=cluster_ids, cluster_type=ClusterType.TenDBCluster.value
6869
).prefetch_related(
6970
"proxyinstance_set",
7071
"storageinstance_set",

dbm-ui/backend/flow/engine/bamboo/scene/tendbsingle/standardize.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@ def __init__(self, root_id: str, data: Optional[Dict]):
5050

5151
def standardize(self):
5252
cluster_ids = self.data["infos"]["cluster_ids"]
53-
bk_biz_id = self.data["bk_biz_id"]
53+
# 支持跨业务
54+
# bk_biz_id = self.data["bk_biz_id"]
5455

5556
cluster_objects = Cluster.objects.filter(
56-
pk__in=cluster_ids, bk_biz_id=bk_biz_id, cluster_type=ClusterType.TenDBSingle.value
57+
pk__in=cluster_ids, cluster_type=ClusterType.TenDBSingle.value
5758
).prefetch_related("storageinstance_set", "storageinstance_set__machine")
5859
if cluster_objects.count() != len(cluster_ids):
5960
raise DBMetaException(

dbm-ui/backend/iam_app/handlers/drf_perm/base.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,26 @@ def has_authenticated_permission(self, request, view):
276276

277277
def has_permission(self, request, view):
278278
return permissions.IsAuthenticated().has_permission(request, view)
279+
280+
281+
class ListResourcePermission(ResourceActionPermission):
282+
"""
283+
业务相关动作的鉴权
284+
"""
285+
286+
def __init__(
287+
self, actions: List[ActionMeta] = None, resource_meta: ResourceMeta = None, bk_biz_id: int = None
288+
) -> None:
289+
self.bk_biz_id = bk_biz_id
290+
if not self.bk_biz_id:
291+
self.actions = ActionEnum.GLOBAL_MANAGE
292+
self.resource_meta = None
293+
else:
294+
self.actions = actions or [ActionEnum.DB_MANAGE]
295+
self.resource_meta = resource_meta or ResourceEnum.BUSINESS
296+
super().__init__(self.actions, self.resource_meta, instance_ids_getter=self.instance_biz_id_getter)
297+
298+
def instance_biz_id_getter(self, request, view):
299+
if not self.resource_meta:
300+
return []
301+
return self.get_key_id(request, view, self.resource_meta.id, many=True)

0 commit comments

Comments
 (0)