- 后端
- 企业应用
- 运维
【免费下载链接】bk-cmdb
蓝鲸智云配置平台(BlueKing CMDB)
本文以蓝鲸智云配置平台(BlueKing CMDB,bk-cmdb)开放 API 文档docs/apidoc/apigw/open/en/add_host_lock.md为核心,系统讲解「锁定主机(Lock hosts)」接口的调用方式、参数约束、响应格式,并结合开源仓库源码剖析其幂等实现、事务处理与权限校验链路。读者读完可准确调用该接口实现主机批量锁定,并理解锁定数据在cc_HostLock表中的存储形态,为上层运维流程(如变更保护、故障隔离)提供可靠依据。
一、接口功能与适用场景
锁定主机是 CMDB 对主机实例提供的一种保护性操作:调用方传入一批主机 ID,即可为这些主机添加"锁定"标记。被锁定的主机在业务层面通常意味着"禁止变更、禁止回收、暂停操作",常用于:
- 变更窗口期的主机保护,防止自动化流程误操作;
- 故障主机隔离,标记后避免被纳入正常调度;
- 资源生命周期管理,锁定待下线/待回收资源。
该接口的官方定义(源自 add_host_lock.md)明确指出:
Lock hosts based on a list of host IDs. For newly added hosts, if the host has already been locked, it will also indicate successful locking。
即:接口按主机 ID 列表锁定主机;对于新加入列表的主机,即使其此前已经被锁定,接口依然返回锁定成功——这是理解该接口幂等语义的关键(详见下文第六节)。
二、接口基本信息
| 项目 | 内容 |
|---|---|
| 接口名称 | add_host_lock(锁定主机) |
| 引入版本 | v3.8.6 |
| 所需权限 | 业务主机编辑权限(Business host editing permission) |
| 请求方式 | POST |
| 请求路径 | /api/v3/host/lock |
从源码看,该路径在 host_server 服务初始化 中注册:
utility.AddHandler(rest.Action{Verb: http.MethodPost, Path: "/host/lock", Handler: s.LockHost}) utility.AddHandler(rest.Action{Verb: http.MethodDelete, Path: "/host/lock", Handler: s.UnlockHost}) utility.AddHandler(rest.Action{Verb: http.MethodPost, Path: "/host/lock/search", Handler: s.QueryHostLock})权限控制方面,ac/parser/host.go 中针对/api/v3/host/lock(POST 锁定、DELETE 解锁)与/api/v3/host/lock/search(查询)均配置了主机实例资源的鉴权过滤,而 service/hostlock.go 中通过AuthorizeByHostsIDs校验「编辑」动作权限,无权限时返回ac.NoAuthorizeError及无权限提示信息,对应文档所述的"业务主机编辑权限"。
三、请求参数详解
3.1 参数总览
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id_list | int 数组 | 是 | 主机 ID 列表(Host IDs) |
3.2 参数约束(源码级验证)
请求体在服务端被反序列化为metadata.HostLockRequest,其定义见 src/common/metadata/hostlock.go:
type HostLockRequest struct { IDS []int64 `json:"id_list"` }服务入口 LockHost 对参数做了第一层校验:
input := &metadata.HostLockRequest{} if err := ctx.DecodeInto(&input); nil != err { ctx.RespAutoError(err) return } if 0 == len(input.IDS) { blog.Errorf("lock host, id_list is empty,input:%+v, rid:%s", input, ctx.Kit.Rid) ctx.RespAutoError(ctx.Kit.CCError.Errorf(common.CCErrCommParamsNeedSet, "id_list")) return }由此可确认两个硬性约束:
id_list不能为空:空数组会直接返回参数错误(错误码CCErrCommParamsNeedSet,提示字段为id_list);- 元素类型为整数(int64):非整数会导致 JSON 反序列化失败,同样走
RespAutoError分支返回错误; - 重复 ID 会被自动去重:核心实现中首先执行
input.IDS = util.IntArrayUnique(input.IDS)(见 core/host/lock.go),因此传入[1, 1, 2]与[1, 2]效果等价。
四、请求示例
按照开放 API 文档,请求体为一个包含id_list字段的 JSON 对象:
{ "id_list":[1, 2, 3] }实际调用(POST):
POST /api/v3/host/lock Content-Type: application/json { "id_list":[1, 2, 3] }五、响应示例与响应参数
5.1 响应示例
成功响应(与文档一致):
{ "result": true, "code": 0, "message": "success", "data": null, "permission": null }5.2 响应参数说明
| 名称 | 类型 | 说明 |
|---|---|---|
| result | bool | 请求是否成功。true:成功;false:失败 |
| code | int | 错误码。0 表示成功,>0 表示失败的具体错误码 |
| message | string | 请求失败时返回的错误信息 |
| data | object | 请求返回的数据(本接口成功时为 null) |
| permission | object | 权限信息 |
需要注意:文档中的响应示例将data置为null,这与服务端实现一致——LockHost 服务处理函数 在成功时调用ctx.RespEntity(nil)返回空数据;核心层 coreservice 的 LockHost 同样以ctx.RespEntity(nil)结束,因此data字段为空是正常预期,业务方无需解析该字段。
若请求失败,code将返回非 0 错误码,message携带具体错误描述。常见的失败场景包括:id_list为空、主机 ID 不存在(见第六节)、鉴权失败(无业务主机编辑权限)等。
六、底层实现原理:幂等、存在性校验与事务
6.1 调用链全景
锁定主机请求在微服务架构中经历三层调用:
- host_server 服务层(service/hostlock.go):解析参数、校验权限、开启事务;
- host_server 逻辑层(logics/hostlock.go):通过
CoreService().Host().LockHost发起对 coreservice 的 HTTP 调用; - coreservice 核心层(core/host/lock.go):直接操作 MongoDB,完成实际的锁定写入。
其中逻辑层封装如下:
func (lgc *Logics) LockHost(kit *rest.Kit, input *metadata.HostLockRequest) errors.CCError { hostLockResult, err := lgc.CoreAPI.CoreService().Host().LockHost(kit.Ctx, kit.Header, input) if nil != err { ... return kit.CCError.Error(common.CCErrCommHTTPDoRequestFailed) } if !hostLockResult.Result { ... return kit.CCError.New(hostLockResult.Code, hostLockResult.ErrMsg) } return nil }6.2 幂等语义:已锁定主机重复锁定仍返回成功
文档强调"if the host has already been locked, it will also indicate successful locking",其实现位于 core/host/lock.go:核心层先按主机 ID 查询cc_HostLock表中是否已存在锁定记录,仅对未锁定的主机追加写入:
for _, id := range input.IDS { conds := mapstr.MapStr{ common.BKHostIDField: id, } conds = util.SetQueryOwner(conds, kit.SupplierAccount) cnt, err := mongodb.Client().Table(common.BKTableNameHostLock).Find(conds).Count(kit.Ctx) ... if 0 == cnt { insertDataArr = append(insertDataArr, metadata.HostLockData{ User: user, ID: id, CreateTime: ts, OwnerID: httpheader.GetSupplierAccount(kit.Header), }) } }也就是说:对已锁定主机,跳过写入但不报错,接口整体仍返回成功,实现"重复锁定幂等"。
6.3 主机存在性校验
在写入锁定之前,核心层会先校验主机是否真实存在(core/host/lock.go):
condition := mapstr.MapStr{ common.BKHostIDField: mapstr.MapStr{common.BKDBIN: input.IDS}, } condition = util.SetQueryOwner(condition, kit.SupplierAccount) hostInfos := make([]metadata.HostMapStr, 0) err := mongodb.Client().Table(common.BKTableNameBaseHost).Find(condition). Fields(common.BKHostIDField).Limit(limit).All(kit.Ctx, &hostInfos) ... diffID := diffHostLockID(input.IDS, hostInfos, kit.Rid) if 0 != len(diffID) { blog.Errorf("lock host, not found, id: %+v, rid: %s", diffID, kit.Rid) return kit.CCError.Errorf(common.CCErrCommParamsIsInvalid, fmt.Sprintf(" id_list %v", diffID)) }diffHostLockID(同文件 L121-L139)将请求 ID 与基础主机表cc_HostBase中实际存在的主机 ID 求差集:只要存在任意一个主机 ID 在 CMDB 中不存在,整个请求即失败,并在错误信息中明确指出不存在的主机 ID。因此调用方需确保传入的 ID 均为主机表内有效 ID。
6.4 事务保证
服务层将核心写入包在事务中执行(service/hostlock.go):
txnErr := s.Engine.CoreAPI.CoreService().Txn().AutoRunTxn(ctx.Kit.Ctx, ctx.Kit.Header, func() error { err := s.Logic.LockHost(ctx.Kit, input) if nil != err { return err } return nil }) if txnErr != nil { ctx.RespAutoError(txnErr) return } ctx.RespEntity(nil)任何一步失败都会触发事务回滚,保证"校验—写入"过程的一致性。
七、锁定数据的存储结构
锁定记录存储在 MongoDB 集合cc_HostLock中,表名常量定义见 src/common/tablenames.go:
BKTableNameHostLock = "cc_HostLock"单条锁定记录(metadata.HostLockData,见 src/common/metadata/hostlock.go)包含四个字段:
| 字段(bson/json) | 类型 | 说明 |
|---|---|---|
| bk_host_id | int64 | 被锁定主机 ID |
| bk_user | string | 发起锁定操作的用户 |
| create_time | time.Time | 锁定创建时间(UTC) |
| bk_supplier_account | string | 供应商账号(多租户隔离字段) |
存储层写入时(core/host/lock.go),用户信息取自请求 Header(httpheader.GetUser),时间统一取time.Now().UTC(),并以SetQueryOwner/SetModOwner注入供应商账号,实现多租户数据隔离。
该表由升级脚本自动创建并维护索引。升级代码见 upgrader/y3.9.202010211805/add_host_lock_table.go:若表不存在则创建,并为bk_host_id建立名为bk_host_id_1的非唯一索引。索引的规范化注册见 src/common/index/collections/hostlock.go,方便按主机 ID 快速查询锁定状态。
八、配套接口:解锁与锁定查询
该接口同属主机锁定 API 组,配套两个接口共同构成完整的锁定管理能力(路由注册见 service_initfunc.go):
| 操作 | 方法 | 路径 | 服务处理函数 |
|---|---|---|---|
| 锁定主机(本接口) | POST | /api/v3/host/lock | LockHost |
| 解锁主机 | DELETE | /api/v3/host/lock | UnlockHost |
| 查询主机锁定状态 | POST | /api/v3/host/lock/search | QueryHostLock |
- 解锁:同样接收
id_list参数,从cc_HostLock表批量删除锁定记录(core/host/lock.go),权限校验与事务机制与锁定一致; - 查询:返回每个主机 ID 的锁定状态(
map[int64]bool),逻辑层 logics/hostlock.go 将未锁定主机置为false、已锁定主机置为true。
这三者共享同一套鉴权(业务主机编辑权限)与数据模型(cc_HostLock),建议联动使用。
九、调用注意事项
- 必填且非空:
id_list为必填字段,空数组会返回参数校验错误; - ID 必须真实存在:任一主机 ID 不存在即整体失败,且错误信息会列出不存在的 ID;建议调用前先通过主机查询接口确认 ID 有效性;
- 幂等友好:重复锁定已锁定主机不会报错,可安全重试;
- 权限前置:调用方需具备对应业务的主机编辑权限,无权限时返回
permission信息(ac.NoAuthorizeError); - 版本门槛:该接口自 v3.8.6 起提供,低版本环境不适用;
- 多租户隔离:锁定记录按
bk_supplier_account隔离,跨供应商账号的 ID 校验与写入互不影响。
综合来看,add_host_lock 是 CMDB 主机保护机制的基础 API,其幂等写入、存在性校验、事务封装与多租户隔离的实现均可在 src/scene_server/host_server/service/hostlock.go、src/source_controller/coreservice/core/host/lock.go 与 src/common/metadata/hostlock.go 中追溯验证,是理解 CMDB 主机域写入类接口设计范式的良好范例。
- 后端
- 企业应用
- 运维
【免费下载链接】bk-cmdb
蓝鲸智云配置平台(BlueKing CMDB)
相关推荐
蓝鲸智云配置平台 bk-cmdb 主机身份查询接口 search_hostidentifier 实战指南
蓝鲸智云配置平台 bk cmdb 主机身份查询接口 search_hostidentifier 实战指南 本指南以蓝鲸智云配置平台(bk cmdb)对外 API
后端企业应用运维蓝鲸配置平台(bk-cmdb)主机身份下发:push_host_identifier 接口原理与实战指南
蓝鲸配置平台(bk cmdb)主机身份下发:push_host_identifier 接口原理与实战指南 导读 主机身份(host identifier)是蓝鲸
后端企业应用运维蓝鲸配置平台 BK-CMDB 主机身份推送结果查询接口 find_host_identifier_push_result 实战指南
蓝鲸配置平台 BK CMDB 主机身份推送结果查询接口 find_host_identifier_push_result 实战指南 导读 find_host_i
后端企业应用运维
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考