- 后端
- 企业应用
- 运维
【免费下载链接】bk-cmdb
蓝鲸智云配置平台(BlueKing CMDB)
导读
list_biz_hosts_topo是蓝鲸智云配置平台(BlueKing CMDB,即 bk-cmdb)对外提供的 OpenAPI 网关接口之一,用于按业务 ID 一次性查询该业务下的主机明细及其所属的集群、模块拓扑关系,并支持对集群、模块、主机三个维度的属性进行组合过滤。读完本文,你将掌握该接口的完整请求参数结构、querybuilder组合查询规则的写法(含嵌套与操作符取值)、返回数据结构语义,以及它在host_server服务内部的完整执行链路与底层实现原理,可直接据此编写可运行的调用代码。
本文对应接口文档为 docs/apidoc/apigw/open/zh/list_biz_hosts_topo.md,英文版见 docs/apidoc/apigw/open/en/list_biz_hosts_topo.md。接口文档按 API Gateway 资源文档规范组织,存放于仓库的 API Gateway 接口文档目录,参见 docs/apidoc/apigw/README.md。
接口概述
| 项 | 说明 |
|---|---|
| 接口名称 | list_biz_hosts_topo |
| 请求方法 | POST |
| 路由(host_server 内部) | /hosts/app/{bk_biz_id}/list_hosts_topo |
| 权限 | 业务访问权限(Business Access Permission) |
| 核心能力 | 按业务查询主机明细 + 主机所属集群/模块拓扑,三个维度可叠加过滤 |
该接口的路由注册位于 src/scene_server/host_server/service/service_initfunc.go,处理函数为ListBizHostsTopo,定义于 src/scene_server/host_server/service/findhost.go。
输入参数说明
顶层参数
| 参数名称 | 参数类型 | 必选 | 描述 |
|---|---|---|---|
| page | object | 是 | 分页查询条件,返回的主机数据按照bk_host_id排序 |
| bk_biz_id | int | 是 | 业务 ID(同时作为 URL 路径参数{bk_biz_id}) |
| set_property_filter | object | 否 | 集群属性组合查询条件 |
| module_property_filter | object | 否 | 模块属性组合查询条件 |
| host_property_filter | object | 否 | 主机属性组合查询条件 |
| fields | array | 是 | 主机属性列表,控制返回结果的主机里有哪些字段,能够加速接口请求和减少网络流量传输 |
需要说明的是,bk_biz_id同时出现在请求体与路径中。从源码看,处理函数优先从路径参数解析业务 ID(findhost.go),因此调用时务必保证路径与请求体中的业务 ID 一致。
page 分页参数
| 参数名称 | 参数类型 | 必选 | 描述 |
|---|---|---|---|
| start | int | 是 | 记录开始位置 |
| limit | int | 是 | 每页限制条数,最大 500 |
分页校验在参数结构体ListBizHostsTopoParameter.Validate中完成(src/common/metadata/hostserver.go):page.limit必须设置(不能为 0),且不能超过常量common.BKMaxInstanceLimit(即 500),否则返回参数校验错误。返回的主机数据按bk_host_id排序。
set_property_filter / module_property_filter / host_property_filter
这三个参数结构完全一致,分别作用于集群、模块、主机三层维度,用于"根据某层属性字段筛选出该层实例,再定位其下/其上的主机"。
- 组合支持
AND和OR两种方式,可以嵌套,最多嵌套 2 层; - 过滤规则为四元组
field、operator、value。
组合节点(condition / rules)
| 参数名称 | 参数类型 | 必选 | 描述 |
|---|---|---|---|
| condition | string | 否 | 组合查询条件,取值AND/OR |
| rules | array | 否 | 规则数组,元素可以是原子规则,也可以是嵌套的组合节点 |
原子规则(rules 元素)
| 参数名称 | 参数类型 | 必选 | 描述 |
|---|---|---|---|
| field | string | 是 | 字段名 |
| operator | string | 是 | 操作符,可选值equal,not_equal,in,not_in,less,less_or_equal,greater,greater_or_equal,between,not_between |
| value | - | 否 | 操作数,不同 operator 对应不同的 value 格式 |
组装规则的完整语法与操作符语义,可参考 src/common/querybuilder/README.md,以及对应实现 src/common/querybuilder/types.go 与 src/common/querybuilder/parser.go。
组合查询规则(querybuilder)详解
接口文档中提到的三个*_property_filter参数在底层均由configcenter/src/common/querybuilder包解析。QueryFilter的 JSON/BSON 反序列化即调用ParseRule将请求数据解析为规则树(parser.go),因此文档给出的嵌套 JSON 可以直接被接口解析。
规则模型:原子规则与组合规则
从源码(types.go)可以看到,规则树由两种节点组成:
- 原子规则(AtomRule):叶子节点,由
field、operator、value三元组构成,是任何过滤规则的基础; - 组合规则(CombinedRule):非叶子节点,由
condition(AND/OR)与rules(可继续嵌套的规则数组)构成,组合的节点可以是原子规则或组合规则。
每个节点实现了统一的Rule接口:GetDeep()返回规则深度、Validate()做合法性校验、ToMgo()将规则转换为 MongoDB 查询条件、Match()用于内存匹配。这就是"查询条件本质上是过滤规则树"的底层模型。
嵌套深度限制的真相
文档说"最多嵌套 2 层",其实现依据是MaxDeep = 3常量(types.go),注释明确说明:"嵌套层级的深度按树的高度计算,查询条件最大深度为 3 即最多嵌套 2 层"。在 ListBizHostsTopoParameter.Validate 中,三个 filter 分别调用GetDeep()并与MaxDeep比较,超限即返回host_property_filter.rules/set_property_filter.rules/module_property_filter.rules超限错误。
操作符说明
接口文档给出的操作符可选值为:equal、not_equal、in、not_in、less、less_or_equal、greater、greater_or_equal、between、not_between。
需要指出的是,src/common/querybuilder/README.md 中明确说明:该模块不支持between与not_between运算符,这类运算可以基于基本比较运算符组合实现。与此同时,实现还额外支持时间操作符(datetime_less等,仅支持 RFC3339 格式)、字符串操作符(begins_with、contains、ends_with等)、空值操作符(is_null、is_not_null)、字段存在操作符(exist、not_exist)以及数组空值操作符(is_empty、is_not_empty),详见 README 的 Operator 章节 与 types.go 的校验与转换实现。
各操作符与 value 格式的对应关系如下:
| 操作符 | 含义 | value 格式 |
|---|---|---|
| equal | 相等比较 | 基本数据类型(数值/bool/字符串) |
| not_equal | 不等比较 | 基本数据类型(数值/bool/字符串) |
| in | 字段值在指定集合中 | 同类型基本数据类型组成的数组 |
| not_in | 字段值不在指定集合中 | 同类型基本数据类型组成的数组 |
| less | 字段值 < value | 数值 |
| less_or_equal | 字段值 <= value | 数值 |
| greater | 字段值 > value | 数值 |
| greater_or_equal | 字段值 >= value | 数值 |
| is_empty / is_not_empty | 数组为空 / 非空 | 不接受参数 |
| is_null / is_not_null | 字段值为 null / 非 null | 不接受参数 |
| exist / not_exist | 字段存在 / 不存在 | 不接受参数 |
| begins_with / not_begins_with | 以某字符串开头 / 不以某字符串开头 | 非空字符串 |
| contains / not_contains | 包含 / 不包含某字符串 | 非空字符串 |
| ends_with / not_ends_with | 以某字符串结尾 / 不以某字符串结尾 | 非空字符串 |
| datetime_less / datetime_less_or_equal / datetime_greater / datetime_greater_or_equal | 时间早于 / 不晚于 / 晚于 / 不早于 | RFC3339 格式字符串 |
校验规则可参见 types.go 的validateValueByOperator:数组类操作符要求数组元素类型一致(接口通过RuleOption{NeedSameSliceElementType: true}开启该校验),数值类操作符要求 value 为数值,字符串类操作符要求非空字符串。转换到 MongoDB 查询条件时,equal/not_equal/in/not_in会先调用ConvertIpv6ToFullWord做 IPv6 全字格式转换(types.go),这保证了 IPv6 地址字段过滤的正确性。
调用示例
以下为接口文档给出的完整请求体(可直接用于 POST 调用):
{ "page": { "start": 0, "limit": 10 }, "bk_biz_id": 3, "fields": [ "bk_host_id", "bk_cloud_id", "bk_host_innerip", "bk_os_type", "bk_mac" ], "set_property_filter": { "condition": "AND", "rules": [ { "field": "bk_set_name", "operator": "not_equal", "value": "test" }, { "condition": "OR", "rules": [ { "field": "bk_set_id", "operator": "in", "value": [ 1, 2, 3 ] }, { "field": "bk_service_status", "operator": "equal", "value": "1" } ] } ] }, "module_property_filter": { "condition": "OR", "rules": [ { "field": "bk_module_name", "operator": "equal", "value": "test" }, { "condition": "AND", "rules": [ { "field": "bk_module_id", "operator": "not_in", "value": [ 1, 2, 3 ] }, { "field": "bk_module_type", "operator": "equal", "value": "1" } ] } ] }, "host_property_filter": { "condition": "AND", "rules": [ { "field": "bk_host_innerip", "operator": "equal", "value": "127.0.0.1" }, { "condition": "OR", "rules": [ { "field": "bk_os_type", "operator": "not_in", "value": [ "3" ] }, { "field": "bk_cloud_id", "operator": "equal", "value": 0 } ] } ] } }示例语义拆解
- page:从第 0 条开始,每页最多返回 10 条,按
bk_host_id排序; - bk_biz_id:业务 ID 为 3;
- fields:只返回
bk_host_id、bk_cloud_id、bk_host_innerip、bk_os_type、bk_mac五个主机字段,减少网络传输; - set_property_filter:集群名不等于
test,且(集群 ID 在 1/2/3 中 或 服务状态等于1); - module_property_filter:模块名等于
test,或(模块 ID 不在 1/2/3 中 且 模块类型等于1); - host_property_filter:内网 IP 等于
127.0.0.1,且(系统类型不在["3"]中 或 管控区域等于0)。
该示例同时演示了三个 filter 各自带一层嵌套(AND下挂OR,或OR下挂AND),正好落在"最多嵌套 2 层"的约束之内。
响应示例
{ "result": true, "code": 0, "message": "success", "permission": null, "data": { "count": 3, "info": [ { "host": { "bk_cloud_id": 0, "bk_host_id": 1, "bk_host_innerip": "192.168.15.18", "bk_mac": "", "bk_os_type": null }, "topo": [ { "bk_set_id": 11, "bk_set_name": "set1", "module": [ { "bk_module_id": 56, "bk_module_name": "m1" } ] } ] }, { "host": { "bk_cloud_id": 0, "bk_host_id": 2, "bk_host_innerip": "192.168.15.4", "bk_mac": "", "bk_os_type": null }, "topo": [ { "bk_set_id": 11, "bk_set_name": "set1", "module": [ { "bk_module_id": 56, "bk_module_name": "m1" } ] } ] }, { "host": { "bk_cloud_id": 0, "bk_host_id": 3, "bk_host_innerip": "192.168.15.12", "bk_mac": "", "bk_os_type": null }, "topo": [ { "bk_set_id": 10, "bk_set_name": "空闲机池", "module": [ { "bk_module_id": 54, "bk_module_name": "空闲机" } ] } ] } ] } }从响应可以看到:每台主机通过host对象携带其实际属性数据,通过topo数组携带所属的集群与模块信息;未分配具体业务模块的主机(如第 3 台)会出现在"空闲机池 / 空闲机"这类默认拓扑节点下。
响应参数说明
公共响应包装
| 参数名称 | 参数类型 | 描述 |
|---|---|---|
| result | bool | 请求成功与否。true:请求成功;false:请求失败 |
| code | int | 错误编码。0 表示 success,>0 表示失败错误 |
| message | string | 请求失败返回的错误信息 |
| permission | object | 权限信息 |
| data | object | 请求返回的数据 |
data
| 参数名称 | 参数类型 | 描述 |
|---|---|---|
| count | int | 记录条数 |
| info | array | 主机数据和拓扑信息 |
data.info
| 参数名称 | 参数类型 | 描述 |
|---|---|---|
| host | dict | 主机实际数据 |
| topo | array | 主机拓扑信息 |
data.info.host(系统内置主机属性)
| 参数名称 | 参数类型 | 描述 |
|---|---|---|
| bk_host_name | string | 主机名 |
| bk_host_innerip | string | 内网 IP |
| bk_host_id | int | 主机 ID |
| bk_cloud_id | int | 管控区域 |
| import_from | string | 主机导入来源,以 api 方式导入为 3 |
| bk_asset_id | string | 固资编号 |
| bk_cloud_inst_id | string | 云主机实例 ID |
| bk_cloud_vendor | string | 云厂商 |
| bk_cloud_host_status | string | 云主机状态 |
| bk_comment | string | 备注 |
| bk_cpu | int | CPU 逻辑核心数 |
| bk_cpu_architecture | string | CPU 架构 |
| bk_cpu_module | string | CPU 型号 |
| bk_disk | int | 磁盘容量(GB) |
| bk_host_outerip | string | 主机外网 IP |
| bk_host_innerip_v6 | string | 主机内网 IPv6 |
| bk_host_outerip_v6 | string | 主机外网 IPv6 |
| bk_isp_name | string | 所属运营商 |
| bk_mac | string | 主机内网 MAC 地址 |
| bk_mem | int | 内存容量(MB) |
| bk_os_bit | string | 操作系统位数 |
| bk_os_name | string | 操作系统名称 |
| bk_os_type | string | 操作系统类型 |
| bk_os_version | string | 操作系统版本 |
| bk_outer_mac | string | 主机外网 MAC 地址 |
| bk_province_name | string | 所在省份 |
| bk_service_term | int | 质保年限 |
| bk_sla | string | SLA 级别 |
| bk_sn | string | 设备 SN |
| bk_state | string | 当前状态 |
| bk_state_name | string | 所在国家 |
| operator | string | 主要维护人 |
| bk_bak_operator | string | 备份维护人 |
注意:此处的返回值仅对系统内置的属性字段做了说明,其余返回值取决于用户自己定义的属性字段。也就是说,如果业务中为主机模型扩展了自定义属性,只要出现在请求的fields中,同样会返回。
data.info.topo
| 参数名称 | 参数类型 | 描述 |
|---|---|---|
| bk_set_id | int | 主机所属的集群 ID |
| bk_set_name | string | 主机所属的集群名字 |
| module | array | 主机所属的集群下模块信息 |
data.info.topo.module
| 参数名称 | 参数类型 | 描述 |
|---|---|---|
| bk_module_id | int | 模块 ID |
| bk_module_name | string | 模块名字 |
上述响应结构在源码中对应 src/common/metadata/hostserver.go 的HostTopoResult、HostTopo、Topo、Module四个结构体,其 JSON tag 与文档参数一一对应。
源码级执行链路解析
接口处理函数ListBizHostsTopo的内部流程可以划分为六个阶段,对应 findhost.go:
- 路径参数解析与鉴权:从路径中解析
bk_biz_id(为 0 或非法时直接报参数错误),随后调用authHostUnderBiz校验当前用户的业务访问权限,无权限时返回RespNoAuth; - 请求体反序列化与校验:将请求体解析为
meta.ListBizHostsTopoParameter,调用其Validate方法校验分页参数与三个过滤条件(hostserver.go); - 集群维度预过滤:调用
parseHostsTopoFilter(findhost.go)将set_property_filter通过ToMgo()转换为 MongoDB 条件,并与bk_biz_id组成 AND 条件查询集群实例,得到符合条件的setIDs与setMap(ID 到名称的映射)。若配置了该过滤条件但结果为空,直接返回count=0的空列表,避免后续查询; - 模块维度预过滤:同样的逻辑处理
module_property_filter,得到moduleIDs与moduleMap; - 主机查询:以
bizID + setIDs + moduleIDs + host_property_filter组装meta.ListHosts查询条件,调用CoreService().Host().ListHosts查询主机列表。注意这里会把fields与bk_host_id合并后传给底层(保证后续拓扑组装有主机 ID 可用),并设置 MongoDB 从节点读偏好(SecondaryPreferredMode); - 拓扑组装:调用
rearrangeBizHostTopo(findhost.go)补齐拓扑信息——先按主机 ID 集合查询"主机-模块"关系(GetHostRelations),生成主机ID -> 集群ID -> 模块ID列表的映射;再对尚未出现在setMap/moduleMap中的集群、模块实例做一次补充查询(getOtherInstInfo,findhost.go);最后按HostTopoResult{count, info:[{host, topo:[{bk_set_id, bk_set_name, module:[{bk_module_id, bk_module_name}]}]}]}的格式组装输出。
其中集群/模块实例的查询由GetInstIDNameInfo完成(src/scene_server/host_server/logics/instance.go):按objID解析实例 ID 与名称字段(如 set 的bk_set_id/bk_set_name),一次查询出ID -> 名称映射,供拓扑输出使用。
客户端调用方式
在 Go 侧,apimachinery 提供了封装好的客户端方法ListBizHostsTopo(src/apimachinery/hostserver/apis.go),其内部即向/hosts/app/%d/list_hosts_topo发起 POST 请求。
仓库的集成测试 src/test/host_server/host_test.go 给出了典型的调用与断言模式:先通过TransferHostModule将主机转移至目标模块,再调用ListBizHostsTopo并断言响应中同时包含count、两台主机的bk_host_id、所属bk_set_id与bk_module_id。这也验证了该接口"主机 + 拓扑一次返回"的核心行为。
使用建议与注意事项
- 善用 fields 裁剪字段:文档明确说明
fields用于控制返回的主机字段,能加速接口请求并减少网络流量。只声明本次业务需要的字段即可,服务端还会在内部追加bk_host_id用于拓扑组装。 - 保持分页参数在约束内:
page.limit必填且最大 500,start从 0 开始;结果按bk_host_id排序,适合游标式翻页。 - 三过滤器是"逐层收窄"关系:从执行链路看,集群过滤与模块过滤会先各自筛出符合条件的实例 ID 集合,再与主机属性过滤一起作为主机查询的约束条件,三者共同决定最终返回的主机集合;而返回的
topo则是主机实际所属的完整集群/模块关系。 - 注意嵌套深度与操作符边界:组合条件最多嵌套 2 层(源码层面对应
MaxDeep=3的树高限制);同时按 src/common/querybuilder/README.md 的说明,between/not_between未被该模块实现,如需区间查询请用greater_or_equal+less_or_equal组合表达。 - 权限前置:接口要求业务访问权限,无权限的调用会在进入数据查询前被拦截(findhost.go)。
相关资源索引
- 接口中文文档:docs/apidoc/apigw/open/zh/list_biz_hosts_topo.md
- 接口英文文档:docs/apidoc/apigw/open/en/list_biz_hosts_topo.md
- 组合查询规则说明:src/common/querybuilder/README.md
- querybuilder 规则模型与操作符实现:src/common/querybuilder/types.go、src/common/querybuilder/parser.go
- 接口处理实现:src/scene_server/host_server/service/findhost.go
- 参数与响应结构体:src/common/metadata/hostserver.go
- 客户端封装:src/apimachinery/hostserver/apis.go
- 集成测试:src/test/host_server/host_test.go
- 后端
- 企业应用
- 运维
【免费下载链接】bk-cmdb
蓝鲸智云配置平台(BlueKing CMDB)
相关推荐
蓝鲸配置平台 bk-cmdb:主机详情与拓扑树查询接口(findmany/hosts/detail_topo)实战指南
蓝鲸配置平台 bk cmdb:主机详情与拓扑树查询接口(findmany/hosts/detail_topo)实战指南 本文基于蓝鲸智云配置平台(BlueKin
后端企业应用运维蓝鲸智云配置平台 bk-cmdb 主机身份查询接口 search_hostidentifier 实战指南
蓝鲸智云配置平台 bk cmdb 主机身份查询接口 search_hostidentifier 实战指南 本指南以蓝鲸智云配置平台(bk cmdb)对外 API
后端企业应用运维蓝鲸智云配置平台(bk-cmdb)API 实战:查询主机详情及其拓扑信息(list_host_detail_topology)
蓝鲸智云配置平台(bk cmdb)API 实战:查询主机详情及其拓扑信息(list_host_detail_topology) 本文面向蓝鲸智云配置平台(Blu
后端企业应用运维
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考