|
7 | 7 | an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
8 | 8 | specific language governing permissions and limitations under the License. |
9 | 9 | """ |
| 10 | +import copy |
| 11 | +import logging |
| 12 | +import re |
| 13 | +import time |
10 | 14 | from collections import defaultdict |
11 | 15 | from typing import Dict, List |
12 | 16 |
|
13 | 17 | from django.db.models import F |
14 | 18 |
|
| 19 | +from backend import env |
| 20 | +from backend.components import BKMonitorV3Api |
15 | 21 | from backend.configuration.constants import DBType |
16 | 22 | from backend.configuration.models import DBAdministrator |
17 | 23 | from backend.db_meta.enums import ClusterType, MachineType |
18 | 24 | from backend.db_meta.models import AppCache, Cluster, ClusterEntry, Machine, ProxyInstance, StorageInstance |
19 | 25 |
|
| 26 | +logger = logging.getLogger("root") |
| 27 | + |
| 28 | +# 用于从 TS 指标查询 meta 的 unify_query 模板(instant) |
| 29 | +_UNIFY_QUERY_META_PARAMS = { |
| 30 | + "bk_biz_id": 3, |
| 31 | + "query_configs": [ |
| 32 | + { |
| 33 | + "data_source_label": "prometheus", |
| 34 | + "data_type_label": "time_series", |
| 35 | + "promql": "", |
| 36 | + "interval": 60, |
| 37 | + "alias": "a", |
| 38 | + } |
| 39 | + ], |
| 40 | + "expression": "a", |
| 41 | + "alias": "a", |
| 42 | + "start_time": 0, |
| 43 | + "end_time": 0, |
| 44 | + "slimit": 500, |
| 45 | + "type": "instant", |
| 46 | +} |
| 47 | + |
20 | 48 |
|
21 | 49 | def list_my_mongodb_bizs(username: str) -> List: |
22 | 50 | res = [] |
23 | | - for app in AppCache.objects.all(): |
| 51 | + for app in AppCache.objects.all(): # pyright: ignore[reportAttributeAccessIssue] |
24 | 52 | bk_biz_id = app.bk_biz_id |
25 | | - if DBAdministrator.objects.filter( |
| 53 | + if DBAdministrator.objects.filter( # pyright: ignore[reportAttributeAccessIssue] |
26 | 54 | bk_biz_id=bk_biz_id, users__0=username, db_type=DBType.MongoDB.value |
27 | 55 | ).exists(): |
28 | 56 | res.append({"bk_biz_id": bk_biz_id, "app_name": app.bk_biz_name, "abbr": app.db_app_abbr}) |
29 | 57 | return res |
30 | 58 |
|
31 | 59 |
|
32 | 60 | def mongodb_list_clusters(bk_biz_id: int) -> List: |
33 | | - clusters = Cluster.objects.filter( |
| 61 | + clusters = Cluster.objects.filter( # pyright: ignore[reportAttributeAccessIssue] |
34 | 62 | bk_biz_id=bk_biz_id, |
35 | 63 | cluster_type__in=[ClusterType.MongoReplicaSet, ClusterType.MongoShardedCluster], |
36 | 64 | ) |
@@ -88,8 +116,103 @@ def get_machine_stats(all_machine_ids) -> Dict: |
88 | 116 | return machine_distribution |
89 | 117 |
|
90 | 118 |
|
| 119 | +def get_mongodb_meta_from_ts_metric(conds: Dict) -> Dict: |
| 120 | + """ |
| 121 | + 从监控指标 bkmonitor:dbm_system:cpu_summary:usage 的 label 中解析出 (cluster_domain, bk_target_ip, instance_role), |
| 122 | + 再用 DBM 元数据补全 cluster_name、cluster_id、port、bk_biz_id、app_name、shard 等。 |
| 123 | +
|
| 124 | + conds 支持: |
| 125 | + - cluster_domain: 集群域名,仅查询该集群 |
| 126 | + - ip: 主机 IP,仅查询该 IP 上的实例 |
| 127 | + - instance_port: 可选,与 ip 一起时过滤指定端口(通过 DBM 元数据过滤) |
| 128 | + """ |
| 129 | + # mongodb_types = [ClusterType.MongoReplicaSet.value, ClusterType.MongoShardedCluster.value] |
| 130 | + group_by_keys = [ |
| 131 | + "cluster_domain", "bk_target_ip", "instance_role", "shard", "instance_port", |
| 132 | + "cluster_type", "instance_host", "instance", |
| 133 | + ] |
| 134 | + label_filters = [] |
| 135 | + if conds.get("cluster_domain"): |
| 136 | + label_filters.append(f'cluster_domain="{conds["cluster_domain"]}"') |
| 137 | + if conds.get("ip"): |
| 138 | + label_filters.append(f'bk_target_ip="{conds["ip"]}"') |
| 139 | + label_str = ",".join(label_filters) if label_filters else "" |
| 140 | + promql = ( |
| 141 | + f'max by ({",".join(group_by_keys)}) ' |
| 142 | + f'(max_over_time(bkmonitor:dbm_system:cpu_summary:usage{{{label_str}}}[5m]))' |
| 143 | + ) |
| 144 | + params = copy.deepcopy(_UNIFY_QUERY_META_PARAMS) |
| 145 | + params["bk_biz_id"] = env.DBA_APP_BK_BIZ_ID |
| 146 | + end_ts = int(time.time()) |
| 147 | + params["start_time"] = end_ts - 300 |
| 148 | + params["end_time"] = end_ts |
| 149 | + params["query_configs"][0]["promql"] = promql |
| 150 | + |
| 151 | + try: |
| 152 | + resp = BKMonitorV3Api.unify_query(params) |
| 153 | + except Exception as e: |
| 154 | + logger.exception("get_mongodb_meta_from_ts_metric unify_query error: %s", e) |
| 155 | + return {"meta_list": [], "error": str(e)} |
| 156 | + |
| 157 | + series = resp.get("series", []) |
| 158 | + meta_list = [] |
| 159 | + |
| 160 | + for s in series: |
| 161 | + dims = s.get("dimensions") or s.get("group_keys") or s.get("metric") or {} |
| 162 | + cluster_domain = dims.get("cluster_domain") |
| 163 | + bk_target_ip = dims.get("bk_target_ip") |
| 164 | + instance_role = dims.get("instance_role", "") |
| 165 | + instance_port = dims.get("instance_port", "") |
| 166 | + instance = dims.get("instance", "") or f"{bk_target_ip}:{instance_port}" |
| 167 | + instance_host = dims.get("instance_host", "") or f"{bk_target_ip}" |
| 168 | + cluster_type = dims.get("cluster_type", "") |
| 169 | + shard = dims.get("shard", "") |
| 170 | + meta_list.append({ |
| 171 | + "cluster_domain": cluster_domain, |
| 172 | + "cluster_type": cluster_type, |
| 173 | + "bk_target_ip": bk_target_ip, |
| 174 | + "instance_port": instance_port, |
| 175 | + "instance_role": instance_role, |
| 176 | + "instance_host": instance_host, |
| 177 | + "instance": instance, |
| 178 | + "shard": shard, |
| 179 | + }) |
| 180 | + |
| 181 | + return {"meta_list": meta_list, "error": ""} |
| 182 | + |
| 183 | + |
| 184 | +def meta_info(value: str) -> Dict: |
| 185 | + """ |
| 186 | + 根据输入的值,返回对应的元信息(从 TS 指标 bkmonitor:dbm_system:cpu_summary:usage 的 label 解析,再以 DBM 元数据补全)。 |
| 187 | +
|
| 188 | + value 支持: |
| 189 | + - IP:如 "11.177.156.201" |
| 190 | + - IP:PORT:如 "11.177.156.201:50005" |
| 191 | + - 集群域名:如 "mongo.xxx.db"(含点号) |
| 192 | + """ |
| 193 | + try: |
| 194 | + conds = {} |
| 195 | + if value is None: |
| 196 | + return {"meta_list": [], "error": "value is None"} |
| 197 | + value = (value or "").strip() |
| 198 | + if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", value): |
| 199 | + conds["ip"] = value |
| 200 | + elif re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$", value): |
| 201 | + ip, port = value.split(":", 1) |
| 202 | + conds["instance"] = f"{ip}:{port}" |
| 203 | + elif "." in value: |
| 204 | + conds["cluster_domain"] = value |
| 205 | + else: |
| 206 | + return {"meta_list": [], "error": "Invalid value"} |
| 207 | + return get_mongodb_meta_from_ts_metric(conds) |
| 208 | + except Exception as e: |
| 209 | + return {"meta_list": [], "error": f"查询 MongoDB 实例的元数据信息时出错: {str(e)}"} |
| 210 | + |
| 211 | + |
91 | 212 | def cluster_overview(immute_domain: str) -> Dict: |
92 | | - cluster_obj = Cluster.objects.prefetch_related("tags").get(immute_domain=immute_domain) |
| 213 | + cluster_obj = Cluster.objects.prefetch_related("tags").get( # pyright: ignore[reportAttributeAccessIssue] |
| 214 | + immute_domain=immute_domain |
| 215 | + ) |
93 | 216 | stats = { |
94 | 217 | "bk_cloud_id": cluster_obj.bk_cloud_id, |
95 | 218 | "bk_biz_id": cluster_obj.bk_biz_id, |
|
0 commit comments