腾讯蓝鲸CMDB开源代码二次开发输出文档
目录
1 本地启动cmdb
2 cmdb数据库表设计
3 代码模块介绍
4 功能介绍
5 请求调用流程
6 新增接口
7 cmdb如何操作mongo数据库
一 本地启动cmdb
服务器启动回顾
部署cmdb的时候,输入./start.sh 就可以启动12个服务,那么针对这12个服务进行分析一下。首先看一下启动后输出的内容:
这里的配置参数,是根据运行的配置脚本所生成的,如:
生成配置文件位置:
configcenter/src/bin/build/3.2.17/cmdb_adminserver/configures/*
二 打开cmdb项目,把上一步cmdb_adminserver/configures/下生成的所有配置文件覆盖到configcenter/resources/configures/目录中 (即替换配置文件)。
然后修改configcenter/resources/configures/migrate.conf文件
三、本地启动项目
cmdb是微服务框架,把平台拆分成了12个服务,本次我们启动hostServer服务,其他服务启动类似。
启动hostServer的main方法(host.go文件):
启动的的时候配置参数:Program arguments
例如:
--addrport=127.0.0.1:60001 --logtostderr=false --log-dir=./logs --v=3 --regdiscv=127.0.0.1:2181
每个服务启动时 Program arguments 参数不同 可参考服务启动时显示的参数进行配置
二 cmdb数据库表设计
表名 |
关联业务 |
接口 |
备注 |
cc_ApplicationBase |
基础资源-业务信息存储 |
|
|
cc_AsstDes |
模型管理-关联类型数据存储 |
|
|
cc_History |
- |
|
|
cc_HostBase |
基础资源-主机数据存储 |
|
|
cc_HostFavourite |
- |
|
|
cc_idgenerator |
生成ID的,记录每种记录最大的ID,类似mysql 主键的auto_increment |
|
|
cc_InsAsst |
- |
|
|
cc_ModuleBase |
业务资源-业务拓扑中的叶子节点数据 |
|
|
cc_ModuleHostConfig |
主机分配到具体业务模块表,bk_module_id是cc_ModuleBase.bk_module_id |
|
|
cc_Netcollect_Device |
- |
|
|
cc_Netcollect_Property |
- |
|
|
cc_ObjAsst |
模型与模型之间的关联关系,见模型管理-模型拓扑 |
|
|
cc_ObjAttDes |
模型的所有字段 |
|
|
cc_ObjClassification |
存储模型分组数据,针对模型管理-新建分组业务 |
|
|
cc_ObjDes |
存储模型数据,针对模型管理-模型-新建模型业务 |
|
|
cc_ObjectBase |
自定义模型的实例 |
|
|
cc_ObjectUnique |
- |
|
|
cc_UserGroupPrivilege |
当添加用户权限的时候, |
|
通过group_id字段关联 |
cc_UserGroup |
添加用户权限管理 |
|
|
cc_UserCustom |
用户自定义收藏模块 |
|
index_v4_recently |
|
- |
|
|
cc_TopoGraphics |
存储模块在拓扑图中的位置 |
|
|
|
系统 |
|
|
cc_Subscription |
事件推送 |
|
|
cc_SetBase |
业务资源-> 业务拓扑 |
|
|
cc_PropertyGroup |
|
|
|
cc_Process |
业务资源-> 进程管理 存储进程 |
|
bk_biz_id 对应 cc_ApplicationBase |
cc_ProcOpTask |
- |
|
|
cc_ProcInstanceModel |
- |
|
|
cc_ProcInstanceDetail |
- |
|
|
|
- |
|
|
cc_Privilege |
- |
|
|
cc_PlatBase |
- |
|
三 模块介绍
ui: 前端代码
web_server 后端服务,所有请求入口
api_server : 接口层,这一层是系统的api服务网关
scene_server/ 业务场景模块,基于资源管理对应用场景的封装
admin_server 把配置文件放入zk
event_server 事件订阅
host_server 主机
topo_server 模型,实例,业务,模块等
proc_server 进程
source_controller :源管理模块,把资源类型进行了抽象,每一类资源由一类微服务进程来管理
auditController 审计
hostController 主机
objectController 对象
procController 进程
storage: 提供系统所需的资源存储等系统服务
apimachinery : 根据要操作的资源生成新的url,调用source_controller
四、
请求执行过程:
蓝鲸CMDB文档
本地启动cmdb
一、服务器启动回顾
部署cmdb的时候,输入./start.sh 就可以启动12个服务,那么针对这12个服务进行分析一下。首先看一下启动后输出的内容:
这里的配置参数,是根据运行的配置脚本所生成的,如:
生成配置文件位置:
configcenter/src/bin/build/3.2.17/cmdb_adminserver/configures/*
二 打开cmdb项目,把上一步cmdb_adminserver/configures/下生成的所有配置文件覆盖到configcenter/resources/configures/目录中 (即替换配置文件)。
然后修改configcenter/resources/configures/migrate.conf文件
三、本地启动项目
cmdb是微服务框架,把平台拆分成了12个服务,本次我们启动hostServer服务,其他服务启动类似。
启动hostServer的main方法(host.go文件):
启动的的时候配置参数:Program arguments
例如:
--addrport=127.0.0.1:60001 --logtostderr=false --log-dir=./logs --v=3 --regdiscv=127.0.0.1:2181
每个服务启动时 Program arguments 参数不同 可参考服务启动时显示的参数进行配置
CMDB数据库表设计
1表功能
表名 |
关联业务 |
接口 |
备注 |
cc_ApplicationBase |
基础资源-业务信息存储 |
|
|
cc_AsstDes |
模型管理-关联类型数据存储 |
|
|
cc_History |
- |
|
|
cc_HostBase |
基础资源-主机数据存储 |
|
|
cc_HostFavourite |
- |
|
|
cc_idgenerator |
生成ID的,记录每种记录最大的ID,类似mysql 主键的auto_increment |
|
|
cc_InsAsst |
- |
|
|
cc_ModuleBase |
业务资源-业务拓扑中的叶子节点数据 |
|
|
cc_ModuleHostConfig |
主机分配到具体业务模块表,bk_module_id是cc_ModuleBase.bk_module_id |
|
|
cc_Netcollect_Device |
- |
|
|
cc_Netcollect_Property |
- |
|
|
cc_ObjAsst |
模型与模型之间的关联关系,见模型管理-模型拓扑 |
|
|
cc_ObjAttDes |
模型的所有字段 |
|
|
cc_ObjClassification |
存储模型分组数据,针对模型管理-新建分组业务 |
|
|
cc_ObjDes |
存储模型数据,针对模型管理-模型-新建模型业务 |
|
|
cc_ObjectBase |
自定义模型的实例 |
|
|
cc_ObjectUnique |
- |
|
|
cc_UserGroupPrivilege |
当添加用户权限的时候, |
|
通过group_id字段关联 |
cc_UserGroup |
添加用户权限管理 |
|
|
cc_UserCustom |
用户自定义收藏模块 |
|
index_v4_recently |
|
- |
|
|
cc_TopoGraphics |
存储模块在拓扑图中的位置 |
|
|
|
系统 |
|
|
cc_Subscription |
事件推送 |
|
|
cc_SetBase |
业务资源-> 业务拓扑 |
|
|
cc_PropertyGroup |
|
|
|
cc_Process |
业务资源-> 进程管理 存储进程 |
|
bk_biz_id 对应 cc_ApplicationBase |
cc_ProcOpTask |
- |
|
|
cc_ProcInstanceModel |
- |
|
|
cc_ProcInstanceDetail |
- |
|
|
|
- |
|
|
cc_Privilege |
- |
|
|
cc_PlatBase |
- |
|
2 名词介绍
资源名称:
biz: 业务线
set:集群
module:模块
host:主机
字段描述:
bk_supplier_id :供应商ID 默认为0 ,不做变动
bk_host_id : 主机id
bk_obj_id. : 模型id, 模型的唯一标识,如,szone,redis,set,module
关键词:
association 关联类型
object_association模型关联
inst_association 实例关联
privilege 权限
inst 模型实例
object 对象:如 module,set等
classification 模型分类
custom_query:userapi 自定义api
favorites 主机收藏
模块介绍
ui: 前端代码
web_server 后端服务,所有请求入口
api_server : 接口层,这一层是系统的api服务网关
scene_server/ 业务场景模块,基于资源管理对应用场景的封装
admin_server 把配置文件放入zk
event_server 事件订阅
host_server 主机
topo_server 模型,实例,业务,模块等
proc_server 进程
source_controller :源管理模块,把资源类型进行了抽象,每一类资源由一类微服务进程来管理
auditController 审计
hostController 主机
objectController 对象
procController 进程
storage: 提供系统所需的资源存储等系统服务
apimachinery : 根据要操作的资源生成新的url,调用source_controller
功能介绍
1 常用方法
法/文件 |
作用 |
位置 |
详细位置 |
FindSingleObject(bk_obj_id) |
查找是否存在此模型 |
Topo_server |
topo_server /core/operation/object.go |
condition.CreateCondition() |
创建一个结构体存储查询条件 |
common/condition/condition.go |
|
dbInst := logic.Instance. Table(tName). Find(condition). Sort(sort). Start(uint64(skip)). Limit(uint64(limit)) |
将查询条件赋值给结构体赋值 解析: logic 是一个结构体,Instance是结构体中的一个字段 Instance同时还是一个接口,Table是其中一个实现方法 Table()的返回值是Collection Collection是一个结构体,挂在其结体下的方法有find Sort,Start,Limit返回值是 Find这个结构体 |
src/source_controller/hostcontroller/logics/object.go |
GetObjectByCondition()方法 |
dbInst.All() 即Find.All() dbInst.One() 即Find.One() |
查询数据库 解析:one() 和All()这两个方法都挂在Find结构体下,而在上一行中已经将查询条件赋值给Find,所以当调用All方法时,可直接从Find中取值进行查询尽行查询 例如:f.dbc.DB(f.dbname).C(f.collName).Find(f.filter).One(result) |
src/storage/dal/mongo/mgo_mongo.go |
|
2 UI:
前端 VUE:
cmdb vue组件库:https://magicbox.bk.tencent.com/components_vue/2.0/example/index.html#/
前端vue 代码规范使用 Eslint插件,如不按照规范编写则编译报错:
部分规则如下:
不要有多个连续空行。
关键字后面要有一个空格,
函数参数列表括号前面要有一个空格。
始终使用 === 不使用 ==,
逗号后面有一个空格。
else 与它的大括号同行
始终处理函数的 err 参数
浏览器全局变量始终添加前缀 window.
var 声明,每个声明占一行
无多余逗号
逗号必须放在当前行的末尾
. 应当与属性同行
文件以空行结尾
键名和键值之间要有空格
构造函数的名字以大写字母开始
…..等等
若要关闭Eslint验证:
src/ui/build/webpack.base.conf.js 文件中 注释掉eslint
前端请求示例:
请求调用流程: |
getHostList()------> |
search()—> getHostList() ———> |
searchHost() |
|
|
|
方法位置 |
ui/src/views/resource/index.vue |
ui/src/components/hosts/table/index.vue |
ui/src/store/modules/api/host-search.js |
|
|
|
功能 |
页面:获取所有主机 |
调用子组件中的方法 |
接收参数 ,发送http请求 |
|
|
|
文件介绍
位置 |
作用 |
|
|
|
|
src/ui/src/components |
Vue组件,方便复用 |
|
|
|
|
ui/src/store/modules/api/* |
封装URL,接收参数,向后台发请求 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/ui/src/views /* |
所有页面 |
|
|
|
|
ui/src/views/index/index.vue |
首页 |
|
|
|
|
ui/src/views/business/index.vue |
基础资源/业务 |
|
|
|
|
ui/src/views/resource/index.vue |
基础资源/主机 |
|
|
|
|
ui/src/views/hosts/index.vue |
业务资源/主机 |
|
|
|
|
ui/src/views/eventpush/index.vue |
模型管理/事件推送 |
|
|
|
|
ui/src/views/topology/index.vue |
业务资源/业务拓扑 |
|
|
|
|
ui/src/views/custom-query/index.vue |
业务资源/动态分组 |
|
|
|
|
ui/src/views/process/index.vue |
业务资源/进程管理 |
|
|
|
|
ui/src/views/audit/index.vue |
审计与分析/操作审计 |
|
|
|
|
ui/src/views/model-association/index.vue |
模型管理/关联模型 |
|
|
|
|
ui/src/views/model-manage/index.vue |
模型管理/模型 |
|
|
|
|
ui/src/views/model-topology/index.vue |
模型管理/模型拓扑 |
|
|
|
|
ui/src/views/business-model/index.vue |
模型管理/业务模型 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 web_server
文件 |
作用 |
|
|
|
|
src/web_server/service/service.go |
接收所有请求 |
|
|
|
|
src/web_server/middleware/login.go |
中间件:判断请求是否已经登陆,以及是否需要转发到api_server |
|
|
|
|
|
|
|
|
|
|
4 api_server:网关,对请求分类,转发进入sence_server
文件 |
作用 |
||||
api_server/service/v3/uri.go |
对请求进行分类:topo,host,event,proc |
||||
api_server/service/v3/service.go |
根据请求类别,对请求进行转发 |
||||
5 sence_server:业务处理,调用apimachinery 向资源层发起请求
文件 |
做用 |
隶属接口 |
|
|
|
|
|||
XX__server/service/service.go |
接收路由请求,请求入口 |
|
|
|
|
||||
XX__server/service/* |
请求响应方法 |
|
|
|
|
|
|||
topo_server/service/* |
对set,module,biz,privite相关请求进行响应 |
|
|
|
|
|
|
|
|
topo_server/service/inst_module.go topo_server/service/inst_set.go topo_server/service/inst_business |
对模块, 集群, 业务初步逻辑处理 |
|
|
|
|
||||
topo_server/core/operation/inst_module.go |
对modul进一步逻辑处理 |
ModuleOperationInterface |
|
|
|
|
|||
topo_server/core/operation/inst.go |
对逻辑处理结束的对象,向下级服务发起crud请求:module,set,biz,自定义模型,等对象 |
|
|
|
|
||||
topo_server/service/privilege.go |
用户权限相关请求进行响应 |
|
|
|
|
||||
topo_server/core/privilege/privilege.go |
对用户权限的逻辑处理 |
|
|
|
|
|
|
|
|
6 apimachinery :向资源层发起请求
文件/方法 |
作用 |
||||
apimachinery/objcontroller/inst/inst.go |
对object的增删改查请求进行处理:module ,set,biz,自定义对象 等 |
||||
apimachinery/objcontroller/privilege/api.go |
权限,角色相关接口 |
||||
apimachinery/objcontroller/privilege/privilege.go |
权限,角色,相关接口实现方法 |
|
|
|
|
7 controller :对资源(数据库)进行操作
操作方式:
在每个微服务下通常有三个文件夹 :app,service,logics
Service文件夹: 下主要是请求入口,已及请求的响应方法,进行逻辑处理,然后调用logics文件夹下的方法
Logics文件夹: 下通常有一个都有一个logics.go文件,logics.go文件中都有一个结构体Logics 如:
type Logics struct{
Instance dal.RDB
}
Logics文件夹中的所有方法都挂在结构体Logics下,如 func (lgc *Logics) DelModule();
Logics结构体中的Instance 是对数据操作的一些接口如:
type RDB interface {
Clone() RDB
// Table collection 操作
Table(collection string) Table
// StartTransaction 开启新事务
StartTransaction(ctx context.Context) (RDB, error)
}
所以通过结构体Logics,Logics文件夹下的方法可以实现对数据库的操作。
文件 |
作用 |
|
|
|
|
auditcontroller |
收集和查询 操作记录(审计) |
|
|
|
|
hostcontroller |
主机相关操作 |
|
|
|
|
objectcontroller |
Set, module,biz,自定义模型等操作 |
|
|
|
|
proccontroller |
进程相关操作 |
|
|
|
|
source_controller/objectcontroller/service/inst_action.go |
对实例进行增删改查:module,set,biz等 |
|
|
|
|
source_controller/objectcontroller/service/common.go |
对数据库操作的通用方法 |
|
|
|
|
objectcontroller/service/* |
对权限,角色,模型,biz相关接口进行响应 |
|
|
|
|
objectcontroller/service/privilege_action.go |
对用户组权限进行增删改查 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 common
文件 |
|
|
|
|
|
src/common/* |
封装了对数据库操作的方法 |
|
|
|
|
src/common/condition |
封装查询条件,如sort,limit,start,fiels |
|
|
|
|
src/common/conf |
读取解析配置文件 |
|
|
|
|
src/common/mapstr |
将condition的fields字段转换成map |
|
|
|
|
|
|
|
|
|
|
请求调用流程
1 执行流程
HTTP---- /* ----web_server
http请求首先进入web_server 在其中会验证登陆等信息
web_server---------/api/v3/*--------> api_server
web_server中/api/v3的请求会被转发到api_server,在api_server中请求被分类,以host资源为例:/api/v3/* 会转换成/host/v3/* 向下一层服务发起请求
api_server----/host/v3/*------->scene_server----调用--->apimachinery(类似SDK)
api_server向scene_server发起请求,在scene_server中处理业务逻辑,scene_server调用apimachinery向下一级服务发起请求,在apimachinery中url会根据资源类型作出更改,以更改后的url向资源层发请求
apimachinery ---------> source_controller ---------调用------ storage
apimachinery向source_controller发起请求,在source_controller中调用storage封装的方法完成对数据库的操作
-------------------------------------------------------------------------------------------------------
ps:由于api_server只处理url路径是 /api/v3/* 的请求,所以部分请求 url 不是 /api/v3/开头则不会进入api_server,而是进入web_server进行处理。web_server通过调用apimachinery,向source_controller发起请求 来完成对资源的操作。
2 介绍
api_server 将收到的请求处理,分为四类转发进入sence_server下的四个微服务,分别是,topo,host,proc,event 四大类。
例如:/api/v3/biz/...在api_server 中请求会被转成 /topo/v3/biz/...
topo类包括:audit,biz,topo,identifier,inst,module,object,objects,/object/attr,objectatt,set, privilege
host类包括:host,hosts,userapi,usercustom,/findmany/modulehost
event类包括:event
proc类包括:proc
3 CMDB API请求示例
ps:每个微服务下都有一个../service/service.go文件作为请求入口
3.1 获取主机基础信息详情
- API: GET /api/{version}/hosts/{bk_supplier_account}/{bk_host_id}
- http://localhost:8083/api/v3/hosts/0/77
- 功能说明:获取77号主机基础信息详情
- input body: 无
http请求--------> api_server
api_server: 在src/api_server/service/v3/service.go 中接收所有跟路径是/api/v3/ 的请求
请求变成/host/v3/hosts/0/77 进入下一个服务
api_server ------> scene_server :
scene_server: 在src/scene_server/host_server/service/service.go 接收所有跟路径是/host/v3 的路由
其中/hosts/{bk_supplier_account}/{bk_host_id} 接收到请求 并调用
GetHostInstanceProperties()方法:获取参数,处理参数
在 src/apimachinery/hostcontroller/host/api.go文件GetHostByID()方法中 进入下一级服务, 跳转路径:/host/v3/host/77
scene_server-------source_controller:
source_controller:在src/source_controller/hostcontroller/service/service.go文件中接收所有跟路径
为/host/v3的路由,/host/v3/host/{bk_host_id} 接收请求,并调用GetHostByID()方法
最后调用storage,src/storage/dal/mongo/mgo_mongo.go文件中One()方法完成数据库
查询 。
3.2 查询业务线
API: POST /api/{version}/biz/search/{bk_supplier_account}
参数:{ "condition": {}}
condition 参数为业务的任意属性,如果不写代表搜索全部数据
例: http://localhost:8083/api/v3/biz/search/0
apiServer 接收请求并转换成:/topo/v3/app/search/0
topo_server 接收请求并调用 SearchBusiness方法------ 调用FindBusiness()(实现方法在本 页)---- FindOriginInst(实现方法在本页)......
最后调用在 src/apimachinery/objcontroller/inst/inst.go。SearchObjects方法
请求变成: /insts/biz/search
TopoConroller
调用SearchInstObjects方法响应,然后调用src/source_controller/objectcontroller/service/common.go GetObjectByCondition方法
3.3 查询模型实例
API POST /api/{version}/inst/association/search/owner/{bk_supplier_account}/object/{bk_obj_id}
参数{
"condition":{
"bk_weblogic":[
{
"field":"bk_inst_name",
"operator":"$regex",
"value":"qq"
}
]
}
}
例:http://localhost:8083/api/v3/inst/association/search/owner/0/object/redis
api_server 收到请求转化成 /topo/v3/inst/association/search/owner/0/object/redis进入下一服务
业务场景 :topo_server
src/scene_server/topo_server/service/service_initfunc.go 接收请求 转到
SearchInstByAssociation方法--- FindInstByAssociationInst----FindInst----FindOriginInst---
c.clientSet.ObjectController().Instance().SearchObjects()----
src/apimachinery/objcontroller/inst/inst.go SearchObjects()生成URL:/insts/object/search 进入
下一级服务.
资源管理 :object_controller
/insts/{obj_type}/search 接收请求 调用响应方法SearchInstObjects()------ GetHostByCondition---all()
3.4 条件查询主机
API: POST /api/{version}/hosts/search
参数:可根据根据set 、host、biz、module等模型属性进行查询
{
"page":{
"start":0,
"limit":10,
"sort":"bk_host_id"
},
"pattern":"",
"bk_biz_id":2,
"ip":{
"flag":"bk_host_innerip|bk_host_outerip",
"exact":1,
"data":[
]
},
"condition":[
{
"bk_obj_id":"host",
"fields":[
],
"condition":[
]
},
{
"bk_obj_id":"module",
"fields":[
],
"condition":[
]
},
{
"bk_obj_id":"set",
"fields":[
],
"condition":[
]
},
{
"bk_obj_id":"biz",
"fields":[
],
"condition":[
{
"field":"default",
"operator":"$ne",
"value":1
}
]
}
]
}
示例:
localhost:8083/api/v3/hosts/search
参数:{}
api_sever 接收请求 (生成url:/host/v3/hosts/search)---->
sence_service(src/scene_server/host_server/service/service.go )------SearchHost()---s.Logics.SearchHost()----SearchHostByConds()----sh.searchByHostConds()-------sh.lgc.CoreAPI.HostController().Host().GetHosts: path=src/apimachinery/hostcontroller/host/api.go
url==/hosts/search
进入 host_controller
src/source_controller/hostcontroller/service/service.go-----GetHosts()--GetObjectByCondition()---all()
3.5 创建模块
POST /api/{version}/module/{bk_biz_id}/{bk_set_id}
input body:
{
"bk_module_name":"cc_module",
"bk_supplier_account":"0",
"bk_parent_id":0
}
示例:
localhost:8083/api/v3/module/3/2
参数:{ "bk_module_name":"devops"}
http ---------> api_server ---- 跳入下一服务,URL:/topo/v3/module/3/2
//查询模型类型是否存在
topo_server :/module/{app_id}/{set_id} 接收上一请求------>CreateModule()方法
-------FindSingleObject()//查询 --- 跳入下一服务,url:/insts/module/search
object_controller:/insts/{obj_type}/search 接收上一请求------>SearchInstObjects
//创建模型
topo_server: ------ModuleOperation().CreateModule()
object_controller: /insts/{obj_type}接收请求
3.6 创建集群
API: POST /api/{version}/set/{bk_biz_id}
参数
{
"bk_set_name":"wefd", 集群名字
"bk_parent_id":0, 集群上级id 可以是业务线 ,或者其他,具体参照拓扑模型
"bk_biz_id":1 业务线
}
示例:localhost:8083/api/v3/set/3
topo_server /set/{app_id} 接收请求
新增接口流程
1 简介版
查询主机:localhost:8083/api/v3/hosts/selectAll
1 url 设计: 以/api/v3/{type}/…. 开头的请求会被web_server转发进入 api_server
2 type 代表资源类型 根据type 类型 被转发进入sence_server
3 在host_server中编写请求 /host/v3/hosts/selectAll的入口,以及响应方法selectAll(),在selectAll()中处理
调用资源层完成,业务逻辑的处理
4 selectAll()方法调用src/apimachinery 中的httpclient 向controller发起请求
所以在src/apimachinery/hostcontroller/host/api.go 中编写方法 ,使用httpClient向host_controller 发起请
求
请求的url 设计没有规定 如 subPath := "/hosts/myJob”
则完整请求是 localhost:8083/host/v3/hosts/myJob
5 在hostcontroller中 编写请求 /host/v3/hosts/myJob的 路由入口
6 编写myJob的响应方法 查询数据 返回数据
ps : 从第4步开始 src/apimachinery 中的httpclient 向controller发起请求 只是纯粹的对资源进行操作,不包含业务逻辑,所以 一般情况下 只需要变动sence_server,无需变动apimachinery 和controller
2 实操版
1 url 设计: 以/api/v3/{type}/…. 开头的请求会被web_server转发进入 api_server
2 type 代表资源类型 可以为:
biz,topo,identifier,inst,module,objectset,privilege,host,hosts,userapi,usercustom, event proc等,具体请见src/api_server/service/v3/uri.go 中资源划分
3 新增接口查询主机,假设 url设计为:localhost:8083/api/v3/hosts/selectAll,在api_server中 会根据url中的关键词hosts ,判定要操作的资源是host,所以url 从 /api/v3/hosts/selectAll 变成 /host/v3/hosts/selectAll
4 在host_server中,src/scene_server/host_server/service/service.go 接收所有/host/{version}
的请求
所以在此文件中编写/host/v3/hosts/selectAll请求入口
api.Route(api.POST("/hosts/selectAll").To(s.SelectAllHost))
----------------
响应方法:
func (s *Service) SelectAllHost(req *restful.Request, resp *restful.Response) {
//获取头信息
pheader := req.Request.Header
// new 出一个结构体 存储查询参数
body := new(meta.HostCommonSearch)
//获取讲请求中的参数 解析赋值到 结构体body中
json.NewDecoder(req.Request.Body).Decode(body)
// 获得头信息pheader 和 传递的参数body后 调用下个方法SearchHost() 进行查询
host, err := s.Logics.SearchHost(pheader, body)
}
Ps: SearchHost()方法挂在结构体Logics下,Logics结构体中有接口可以调用下一个微服务
func (lgc *Logics)SearchHost(phader http.Header,body Body) (*metadata.GetHostsResult, error) {
//从传入的body中 获取到参数 赋值在 结构体 metadata.QueryInput中
query := metadata.QueryInput{
Start: body.start,
Limit: body.limit,
Sort: common.BKHostInnerIPField,
Fields: common.BKHostInnerIPField,
}
// 把查询参数query 传给apimacheniry,由apimacheniry 向controller发起请求
lgc.CoreAPI.HostController().Host().MyjobSpimachinery()
}
编写 MyjobSpimachinery方法:
在src/apimachinery/hostcontroller/host/api.go 中编写 方法 MyjobSpimachinery(),
在MyjobSpimachinery中
subPath为要跳转的路由入口,可自己设计
metadata.GetHostsResult返回值类型
.Do() 进行跳转
.Into() 把查询结果放进new 出来的结构体,并return
代码如下:
func (t *hostctrl) MyjobSpimachinery(ctx context.Context, h http.Header, opt *metadata.QueryInput) (resp *metadata.GetHostsResult, err error) {
resp = new(metadata.GetHostsResult)
subPath := "/hosts/myJob"
err = t.client.Post().
WithContext(ctx).
Body(opt).
SubResource(subPath).
WithHeaders(h).
Do().
Into(resp)
return
}
-------------------------------------------------------------------
5 在src/source_controller/hostcontroller/service/service.go中接收所有请求是/host/v3 的请求
所以在此文件中编写/hosts/myJob 的入口
api.Route(api.POST("/hosts/myJob").To(s.MyJob))
响应方法:
func (s *Service) MyJob(req *restful.Request, resp *restful.Response) {
//获取参数调用数据库进行查询
}
6.
CMDB中对mongo的操作
1、
cmdb对mongo的常规操作进行了一层封装,方便之后的CRUD,在代码中发现最常见的一种查询方式如下:
lgc.Instance.Table("表名"). Find(map) .All(ctx,&results);
map代表查询条件,通过这行代码完成查询并且把查询结果赋值给results,下面将对这行代码进行解释
2
观察结构体Logics
type Logics struct {
Instance dal.RDB
Cache *redis.Client
*backbone.Engine
}
发现Logics下 Instance dal.RDB 是一个interface ,其实现方法 有对db的一些操作 如下
type RDB interface {
Clone() RDB
// Table collection 操作
Table(collection string) Table
// StartTransaction 开启新事务
StartTransaction(ctx context.Context) (RDB, error)
// Commit 提交事务
Commit(context.Context) error
// Abort 取消事务
Abort(context.Context) error
}
其中Table(collection string) Table是一个interface,其实现方法有
type Table interface {
// Find 查询多个并反序列化到 Result
Find(filter Filter) Find
// Aggregate 聚合查询
AggregateOne(ctx context.Context, pipeline interface{}, result interface{}) error
AggregateAll(ctx context.Context, pipeline interface{}, result interface{}) error
// Insert 插入数据, docs 可以为 单个数据 或者 多个数据
Insert(ctx context.Context, docs interface{}) error
// Update 更新数据
Update(ctx context.Context, filter Filter, doc interface{}) error
// Delete 删除数据
Delete(ctx context.Context, filter Filter) error
}
观察上面结构体总结如下:
Logics 是一个结构体,Instance是结构体中的一个字段。
即:Logics.Instance
Instance同时还是一个接口,Table是其中一个实现方法。
即 :Logics.Instance.Table(“表名”)
Table(name String)的返回值是Collection,Collection是一个结构体指定了要操作的表名。
Find(map)方法挂在Collection结构体下,Find方法将Collection 和map(查询条件)放入Find结构对应的字段。 返回Find结构体 。
即: inst := Logics.Instance.Table(“表名”).Find(map)
此时inst 是一个指定了查寻条件和 要操作的表名的Find结构体
All()方法 挂在Find结构体下:
Logics.Instance.Table(“表名”).Find(map).all(results)
进行最终查询。
在以上的操作中只是为了将各种查询条件放入Find结构中,最终All()方法从Find结构体中取出查询条件进行查询,将结果赋值给results;
All 方法代码如下:
func (f *Find) All(result interface{}) error {
f.dbc.Refresh()
query := f.dbc.DB(f.dbname).C(f.collName).Find(f.filter)
query = query.Select(f.projection)
query = query.Skip(int(f.start))
query = query.Limit(int(f.limit))
query = query.Sort(f.sort...)
//执行查询,若是查询出错 返回错误信息
return query.All(result)
}
实操:
1 查询内网ip是121.21.13.14的主机
首先创建一个map存放查询条件
condition:=make(map[string]interface{})
condition["bk_host_innerip"]="121.21.13.14"
condition["bk_host_id"]="host"
然后定义一个可变的 map数组接收查询结果
results := make([]map[string]interface{}, 0)
最后进行查询
lgc.Instance.
//指定查询的表
Table("cc_HostBase").
//查询条件
Find(condition)
//进行查询
.All(ctx,&results);
find方法只是为了将查询条件赋值给Find结构体,最后的All方法从Find结构体中取出查询条件编写查询语句进行查询
2 插入一条信息进入表cc_HostBase中
//创建插入信息
inputc := input.(map[string]interface{})
inputc[“name”]=“张三"
//开始插入
lgc.Instance.
//指定查询的表
Table("cc_HostBase").
//进行查询
.insert(ctx,inputc);
3 在上文查询语句 Logics.Instance.Table(“表名”).Find(map).all(results)中,将查询条件放在map中,但并不能完全满足需求,例如:查询主机id 不等于"23"的主机,或者大于/小于某个值时都无法用简单的map进行封装查询条件,而在cmdb中还有另外一种存放查询条件的结构体
3.1 查询条件放在结构体condition中
type condition struct {
start int64
limit int64
sort string
fields []Field
or []OR
filterFields []string
}
// 上面这个结构体 调用下面这个接口的实现方法
type Condition interface {
SetPage(page types.MapStr) error
SetStart(start int64)
GetStart() int64
SetLimit(limit int64)
GetLimit() int64
SetSort(sort string)
GetSort() string
SetFields(fields []string)
GetFields() []string
Field(fieldName string) Field
NewOR() OR
Parse(data types.MapStr) error
ToMapStr() types.MapStr
}
接口Condition 有Field 字段,其中字段fields 是个interface 其实现方法有
type Field interface {
Eq(val interface{}) Condition
NotEq(val interface{}) Condition
Like(val interface{}) Condition
In(val interface{}) Condition
NotIn(val interface{}) Condition
Lt(val interface{}) Condition
Lte(val interface{}) Condition
Gt(val interface{}) Condition
NotGt(val interface{}) Condition
Gte(val interface{}) Condition
ToMapStr() types.MapStr
}
所以:cond.Field(“字段").NotEq("条件")
如:
// 创建condition结构体
cond := condition.CreateCondition()
//查询内网ip不等于121.21.13.14的机器
cond.Field(“bk_host_innerIp").NotEq("121.21.13.14")
//创建结构体condMapStr
var condMapStr mapstr.MapStr= mapstr.New()
//将conf 进行一些转化
condMapStr.Merge(cond.ToMapStr())
//condMapStr放着查询条件,作为参数放入Find中进行查询
err:= lgc.Instance.Table("cc_HostBase"). Find(condMapStr) .All(ctx,&results);
Redis在cmdb中的作用
redis在cmdb中主要用于事件订阅:
1 redis的ident文件夹下中存放的信息有:
业务信息,主机信息,模块信息,集群信息,进程信息
2 redis中的信息同步:
2.1 以上信息并非是实时更新,event_server负责从mongo数据库中同步最新的数据,大概每隔1分钟向redis中同步一次信息。
2.2 在event_server启动时调用app.Run()方法------ errCh <- distribution.Start(ctx, cache, db, rpccli)------------chErr <- ih.StartHandleInsts()中代码片段:
go func() {
ih.fetchHostCache()
for range time.Tick(time.Second * 60) {
ih.fetchHostCache()
}
}()
可设置redis中的信息多久和mongo同步一次
2.3 新增订阅事件,删除订阅事件,会时时同步到redis,redis中记录的只是 订阅事件id
3 event_server 直接调用storage 操作数据库,不再调用controller
event_server : 事件订阅服务
请求入口:src/scene_server/event_server/service/service.go:
//查询订阅事件
api.Route(api.POST("/subscribe/search/{ownerID}/{appID}").To(s.Query))
//只测试连通性
api.Route(api.POST("/subscribe/ping").To(s.Ping))
//测试推送
api.Route(api.POST("/subscribe/telnet").To(s.Telnet))
//新增订阅事件
api.Route(api.POST("/subscribe/{ownerID}/{appID}").To(s.Subscribe))
//删除订阅事件
api.Route(api.DELETE("/subscribe/{ownerID}/{appID}/{subscribeID}").To(s.UnSubscribe))
//修改订阅事件
api.Route(api.PUT("/subscribe/{ownerID}/{appID}/{subscribeID}").To(s.Rebook))
4 生产者--消费者
1生产者:
type Service struct {
Core *backbone.Engine
Instance dal.RDB
Cache *redis.Client
}
在结构体Service中有 Cache *redis.Client
当对资源进行增删改查时,在controller中操作成功后,会向redis中新增一个消息 如下:
ec := eventclient.NewEventContextByReq(req.Request.Header, cli.Cache)
ec.InsertEvent(metadata.EventTypeInstData, objType, metadata.EventActionUpdate, newData)
lpush(key,value) 将消息放入redis
这时在redis中,如图4个消息存放于队列中等待被消费:
2 消费者
在 src/scene_server/event_server/identifier/identifier.go :
handleInstLoop方法:
在服务启动的时候 调用handleInstLoop()方法,其中for循环中ih.popEventInst()会和redis建立连接,并从redis中根据key获取到值
for {
event := ih.popEventInst()
if nil == event {
time.Sleep(time.Second * 2)
continue
}
上面for循环内的代码会不停的向redis建立连接取值,当value为空时,继续建立连接会造成资源浪费,所以在ih.popEventInst()中,从redis中取值时并未使用rpop,而是使用了阻塞命令brpop,当value为空时线程会阻塞在那
审计信息的记录
8/10
审计操作是在业务逻辑层完成的操作:
Sence_server ————调用———— src/apimachinery/auditcontroller ——跳转——— audit_controller
在业务逻辑层中(server)处理业务后,根据返回的结果新增操作的审计信息
apimachinery 内封装的有新增审计信息方法:
新增业务操作 AddBusinessLog()
查询审计信息 GetAuditLog ()
新增主机操作 AddHostLog()
新增模块操作 AddModuleLog()
新增对象操作 AddObjectLog()
新增进程操作 AddProcLog()
新增集群操作 AddSetLog()
server 调用apimachinery 封装的方法,上述方法之外的操作不记录审计。
具体新增审计代码(1):
type Service struct {
*options.Config
*backbone.Engine
*logics.Logics
disc discovery.DiscoveryInterface
}
type Engine struct {
sync.Mutex
ServerInfo types.ServerInfo
CoreAPI apimachinery.ClientSetInterface
SvcDisc ServiceDiscoverInterface
Language language.CCLanguageIf
CCErr errors.CCErrorIf
}
aResult, err := service.CoreAPI.AuditController().AddHostLogs(context.Background(), common.BKDefaultOwnerID, appID, user, pheader, log)
通过结构体Service 调用CoreAPI 即apimachinery 内封装的方法
9/10
具体新增审计代码(2)
在topo_server 中
src/scene_server/topo_server/service/service_initfunc.go 可以接收请求有:
新增biz,
新增module,
新增set,
新增Inst
以上请求的响应方法处理逻辑之后,最终都要调用src/scene_server/topo_server/core/operation/inst.go下的
func (c *commonInst) CreateInst(){}方法。
在CreateInst中 新增审计信息 如下:
NewSupplementary().Audit(...).CommitCreateLog(...)
CommitCreateLog方法———commitSnapshot()在commitSnapshot中根据参数不同调用AuditController()中封装好的审计方法
src/common/tablenames.go 所用到的表名,每个表名在这里都有一个变量表示;数据库查询时不直接写查询的表名,而是使用表名所代表的变量
association 关联关系
在topo_server;
src/scene_server/topo_server/service/service_initfunc.go:中有着所有关联关系的入口,分类如下
1 关联类型:创建一种关联关系用于实例间关联事被引用
查询关联类型
"/topo/association/type/action/search
新增关联类型
"/topo/association/type/action/create”
更新一个关联类型
"/topo/association/type/{id}/action/update”
删除一个关联类型
"/topo/association/type/{id}/action/delete”
2 主机和模型实例关联关系
/inst/association/action/search
/inst/association/action/create
/inst/association/{association_id}/action/delete
10/10
3模型拓扑关联关系
/object/association/action/search
/object/association/action/create
/object/association/{id}/action/update
/object/association/{id}/action/delete
权限管理
1 创建角色,添加成员
创建角色时,选择拥有此角色的成员; 也称新建用户分组
topo_server. API: POST /api/{version}/topo/privilege/group/{bk_supplier_account}
参数:分组名,分组成员
{
"group_name":"管理员",
"user_list":"owen;tt"
}
src/apimachinery/objcontroller/privilege/privilege.go: CreateUserGroup() 跳转进入object——controller
src/source_controller/objectcontroller/service/service.go:接收请求
api.Route(api.POST("/privilege/group/{bk_supplier_account}").To(s.CreateUserGroup))
表:cc_Usergroup
2 添加的成员必须时 已经存在的账户 :查询现有的的用户
web_server :ws.GET("/user/list", s.GetUserList)
————— src/web_server/middleware/user/public.go :GetUserList
———— — — src/web_server/middleware/user/plugins/method/self/userinfo.go :GetUserList
在cmdb开源代码中,不支持多用户,在代码固定了一个 admin 用户
3 编辑用户分组权限
API: POST /api/{version}/topo/privilege/group/detail/{bk_supplier_account}/{group_id}
参数:
{bk_middleware: {
bk_tomcat: ["search", "update", "delete"],
redis: ["search", "update", "delete”]
}
}
4 查询用户权限:
API: GET /api/{version}/topo/privilege/user/detail/{bk_supplier_account}/{user_name}
http://localhost:8083/api/v3/topo/privilege/user/detail/0/admin 获取用户admin的权限