cmdb/docs/cmdb_api.md

428 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

**CMDB接口文档v0.1** @ [维易科技](https://veops.cn)
# <div style="text-align: center;">CMDB接口文档</div>
### 一、CI接口
#### 1. CI查询接口
**条件搜索CI**, 按照模型的属性进行条件过滤、统计、排序等查询
* GET `/api/v0.1/ci/s`
* 参数
| 参数名 | 示例值 | 参数类型 | 是否必填 | 参数描述 |
| ---- | ---- | ------ | ------ | ----|
| **q** | q=private_ip:192* | string | 是 | 搜索表达式 |
| **fl** | | string | 否 | 返回的属性字段, 逗号分隔 |
| **facet** | facet=idc | string | 否 | 属性字段,逗号分隔,返回属性对应的所有值的统计 |
| **count** | count=1 | int | 否 | 一页返回的CI数, 默认是25 |
| **page** | page=1 | int | 否 | 页数, 默认是1 |
| **sort** | sort=-private_ip| string | 否 | 属性的排序,降序字段前面加负号- |
| **ret_key** | ret_key=name | enum | 否 | 返回字段类型, 可以是`id`、`name`、`alias`, 默认`name` |
* 参数**q**说明:
* `_type` 指定CI模型, 多个用分号分隔. 例如: `_type:(server;vserver)`
* `attribute:value` 指定属性搜索, `attribute`可以是`id`,`attr_name`和`attr_alias`
* 以上的组合,逗号分隔
* 组合查询使用方法
* **`与`** 关系: `默认关系`
* **`或`**关系: 属性字段前加`-`, 例如: `-hostname:cmdb*`
* **`非`**关系: 属性字段前加`~` 例如: `~hostname:cmdb*`
* **`或非`**关系: 属性字段前加`-~` 例如: `-~hostname:*`
* **`IN`**查询: 例如: `hostname:(cmdb*;cmdb-web*)` 小括号, 分号分隔
* **`范围`**查询: 例如: `hostname:[cmdb* _TO_ cmdb-web*]` `_TO_`分隔
* **`比较`**查询: 例如: `cpu_count:>5` 支持`>, >=, <, <=`
* 结果字段说明
| 字段名 | 值的类型 | 说明 |
| ---- | ---- | ----|
| **numfound** | int | CI总数 |
| **total** | int | 当前页的CI数 |
| **page** | int |分页 |
| **result** | list | 返回的CI列表 |
| **facet** | dict| 根据参数facet做的聚合统计|
| **counter** | dict | 当前页按模型的分类统计 |
* 返回结果
* 搜索示例 `/api/v0.1/ci/s?q=_type:server,private_ip:192.*,idc:*,status:在线&sort=-private_ip&facet=idc&page=1&count=1`
* 返回数据默认json
---
```json
{
"counter": {
"server": 1
},
"facet": {
"idc": [
[
"南汇",
600,
"idc"
],
[
"外高桥",
600,
"idc"
],
[
"张江",
600,
"idc"
]
]
},
"numfound": 1800,
"page": 1,
"result": [
{
"_id": 7238,
"_type": 4,
"buy_date": null,
"ci_type": "server",
"cpu": "Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz",
"cpu_count": 20,
"device_spec": "PowerEdge R630",
"env": "test",
"idc": "外高桥",
"ilo_ip": "192.168.0.120",
"ilo_mac": "82:7b:eb:f8:cb:03",
"kernel_version": "4.1.12-61.1.33.el6uek.x86_64",
"logic_cpu_count": 40,
"maintain_enddate": null,
"maintain_startdate": null,
"manufacturer": "DELL",
"op_duty": "张三",
"os_version": "CentOS Linux release 7.6.1810 (Core)",
"perm": null,
"pos": null,
"private_ip": "192.168.66.99",
"rack": "12086",
"raid": "1.089TB/RAID5",
"ram": "128GB",
"ram_size": "128GB",
"rd_duty": "李四",
"server_name": "192.168.66.99",
"sn": "8cbe16404c11",
"status": "在线",
"unique": "sn",
"vnc_port": null
}
],
"total": 1
}
```
#### 2. 新增CI接口
**创建或者修改CI**
* POST `/api/v0.1/ci`
* 参数
| 参数名 | 示例值 | 参数类型 | 是否必填 | 参数描述 |
| ---- | ---- | ------ | ------ | ----|
| **ci_type** | ci_type=server | string | 是 | 创建CI所属的模型名 |
| **no_attribute_policy** | no_attribute_policy=ignore | string | 否 | 当添加不存在的attribute时的策略, 可选: `reject`、`ignore`, 默认`ignore` |
| **exist_policy** | exist_policy=reject | string | 否 | CI已经存在的处理策略, 可选: `need`、`reject`、`replace` 默认`reject` |
| **模型的属性名** | sn=xxxx | string | 否 | 属性名(id或别名亦可) |
> 注意: 请求的参数里必须包含该CI的唯一标识
* 返回结果
```json
{
"ci_id": 1
}
```
#### 3. 修改CI接口
**修改CI**, 可以使用新增CI的接口, exist_policy=replace, 或者根据ci_id来修改
* PUT `/api/v0.1/ci` 或者 `/api/v0.1/ci/<int:ci_id>`
* 参数
| 参数名 | 示例值 | 参数类型 | 是否必填 | 参数描述 |
| ---- | ---- | ------ | ------ | ----|
| **ci_type** | ci_type=server | string | 是 | 创建CI所属的模型名 |
| **no_attribute_policy** | no_attribute_policy=ignore | string | 否 | 当添加不存在的attribute时的策略, 可选: `reject`、`ignore`, 默认`ignore` |
| **模型的属性名** | sn=xxxx | string | 否 | 属性名(id或别名亦可) |
> 注意: 如果使用`/api/v0.1/ci`, 请求的参数里必须包含该CI的唯一标识
* 返回结果
```json
{
"ci_id": 1
}
```
#### 4. 删除CI接口
**根据ci_id删除CI**, 硬删除操作
* DELETE `/api/v0.1/ci/<int:ci_id>`
* 参数 无
* 返回结果
```json
{
"message": "ok"
}
```
<div STYLE="page-break-after: always;"></div>
### 二、CI关系接口
#### 1. CI关系查询接口
**搜索所有的CI之间的关系**, 比如某一个事业部的所有应用或者是所有服务器
* GET `/api/v0.1/ci_relations/s`
* 参数
| 参数名 | 示例值 | 参数类型 | 是否必填 | 参数描述 |
| ---- | ---- | ------ | ------ | ----|
| **root_id** | root_id=1 | int | 是 | 根节点的ci_id |
| **level** | level=1 | string | 否 | 关系的层级,多层用逗号分隔 |
| **reverse** | reverse=0 | int | 否 | 是否反向搜索, 0或者1, 默认是0, |
| **q** | q=hostname:cmdb* | string | 否 | 搜索表达式 |
| **fl** | | string | 否 | 返回的属性字段, 逗号分隔 |
| **facet** | | string | 否 | 属性字段,逗号分隔,返回属性对应的所有值的统计 |
| **count** | count=25 | int | 否 | 一页返回的CI数, 默认是25 |
| **page** | page=1 | int | 否 | 页数, 默认是1 |
| **sort** | | string | 否 | 属性的排序,降序字段前面加负号- |
| **ret_key** | | enum | 否 | 返回字段类型, 可以是`id`、`name`、`alias`, 默认`name` |
> 搜索表达式`q` 和 `CI查询接口`的搜索表达式q 完全一样!
* 结果字段说明
| 字段名 | 值的类型 | 说明 |
| ---- | ---- | ----|
| **numfound** | int | CI总数 |
| **total** | int | 当前页的CI数 |
| **page** | int |分页 |
| **result** | list | 返回的CI列表 |
| **facet** | dict| 根据参数facet做的聚合统计|
| **counter** | dict | 当前页按模型的分类统计 |
* 返回结果
* 搜索某个事业部下面的物理机 `/api/v0.1/ci_relations/s?root_id=5&level=3&count=1&q=_type:server,idc:南汇`
* 返回数据默认json)
---
```json
{
"counter": {
"server": 1
},
"facet": {},
"numfound": 400,
"page": 1,
"result": [
{
"_id": 159,
"_type": 4,
"bu": null,
"buy_date": null,
"ci_type": "server",
"cmc_ip": null,
"cnc_ip": null,
"cpu": "Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz",
"cpu_count": 20,
"ctc_ip": null,
"device_spec": "PowerEdge R630",
"env": "prod",
"idc": "南汇",
"ilo_ip": "192.168.0.120",
"ilo_mac": "82:7b:eb:f8:cb:03",
"kernel_version": "4.1.12-61.1.33.el6uek.x86_64",
"logic_cpu_count": 40,
"maintain_enddate": null,
"maintain_startdate": null,
"manufacturer": "DELL",
"oneagent_id": null,
"op_duty": "张三",
"os_version": "Microsoft Windows Server 2019 Standard",
"perm": null,
"pos": null,
"private_ip": "192.168.1.2",
"rack": "12086",
"raid": "1.089TB/RAID5",
"ram": "128GB",
"ram_size": "128GB",
"rd_duty": "李四",
"server_name": "192.168.1.2",
"server_room": null,
"sn": "1fd3b1d5c253",
"ssh_port": null,
"status": "在线",
"unique": "sn",
"vnc_port": null
}
],
"total": 1
}
```
#### 2. 增加CI关系接口
**新增CI关系**, 参数`src_ci_id`是源CI的id, `dst_ci_id`是目标CI的id
* POST `/api/v0.1/ci_relations/<int:src_ci_id>/<int:dst_ci_id>`
* 参数 无
* 返回结果
```json
{
"cr_id": 1
}
```
#### 3. 删除CI关系接口
**根据`cr_id`删除CI关系**, 参数`cr_id`是CI关系的id
* DELETE `/api/v0.1/ci_relations/<int:cr_id>`
* 参数 无
* 返回结果
```json
{
"message": "CIType relation deleted"
}
```
<div STYLE="page-break-after: always;"></div>
### 三、响应状态码说明
|状态码|说明|
|----|---|
|200|成功|
|400|请求参数错误或者失败|
|401|未认证|
|403|权限不够|
|404|访问的资源不存在|
|500|服务端未知错误|
|502|服务未启动或者异常退出|
> 所有错误或者失败统一返回json格式为:
```json
{
"message": "错误描述"
}
```
<div STYLE="page-break-after: always;"></div>
### 四、API鉴权方法
- 每个用户会自动生成一个 `api key` 和 一个`secret`, 在ACL系统里可查看到
- 调用API的时候需要提供2个参数 `_key`和`_secret`
- `_key`的值为您的`api key`
- `_secret`的计算方法:
- 除`_key`以外的参数,把**参数名**排序后参数值拼接在一起,并连接到`url path` + `secret`之后
- 求`sha1`**十六进制**值, 即sha1(`url path` + `secret` + `参数名排序后拼接的参数值`)的16进制值
### 五、Python调用样例
#### 鉴权
```python
import hashlib
key = "Your API key"
secret = "Your API secret"
def build_api_key(path, params):
values = "".join([str(params[k]) for k in sorted(params.keys())
if params[k] is not None and not k.startswith('_')]) if params.keys() else ""
_secret = "".join([path, secret, values]).encode("utf-8")
params["_secret"] = hashlib.sha1(_secret).hexdigest()
params["_key"] = key
return params
```
#### 查询
* 以查询CI为例
```python
import hashlib
import requests
from future.moves.urllib.parse import urlparse
URL = "https://demo.veops.cn/api/v0.1/ci/s"
KEY = "Your API key"
SECRET = "Your API secret"
def build_api_key(path, params):
values = "".join([str(params[k]) for k in sorted(params.keys())
if params[k] is not None and not k.startswith('_')]) if params.keys() else ""
_secret = "".join([path, SECRET, values]).encode("utf-8")
params["_secret"] = hashlib.sha1(_secret).hexdigest()
params["_key"] = KEY
return params
def get_ci(payload):
payload = build_api_key(urlparse(URL).path, payload)
return requests.get(URL, params=payload).json()
```
#### 增、删、改
* 以CI的增、删、改为例
```python
import hashlib
import requests
from future.moves.urllib.parse import urlparse
URL = "https://demo.veops.cn/api/v0.1/ci"
KEY = "Your API key"
SECRET = "Your API secret"
def build_api_key(path, params):
values = "".join([str(params[k]) for k in sorted(params.keys())
if params[k] is not None and not k.startswith('_')]) if params.keys() else ""
_secret = "".join([path, SECRET, values]).encode("utf-8")
params["_secret"] = hashlib.sha1(_secret).hexdigest()
params["_key"] = KEY
return params
def add_ci(payload):
payload = build_api_key(urlparse(URL).path, payload)
return requests.post(URL, json=payload).json()
def update_ci(payload, ci_id=None):
url = "{url}/{ci_id}".format(url=URL, ci_id=ci_id) if ci_id is not None else URL
payload = build_api_key(urlparse(url).path, payload)
return requests.put(url, json=payload).json()
def delete_ci(ci_id):
url = "{url}/{ci_id}".format(url=URL, ci_id=ci_id)
payload = build_api_key(urlparse(url).path, {})
return requests.delete(url, json=payload).json()
```