产品 API 使用指南
产品 API 使用指南
一、1 分钟例子
因为 XHERE 的 Auth 方式是使用 API Key,需要在 HTTP 请求的 Header 设置,参数名是 x-sddc-token
,token 值可以通过两种方法获取:
-
通过 SSH 登录已安装 XHERE 的主节点,然后使用命令行创建获取 Raw Token。
-
在 XHERE 管理平台中的 Access Token 界面中创建得到,此方法需要 V2.2 版本才支持。
可以在 XHERE 管理面上的“全局设置”-“访问令牌”页面中,可以生成访问令牌。
获得 token 之后,可以使用 curl
命令,向 XHERE 管理平台发起 HTTP 请求 GET /overview/
。假设 XHERE 集群的主节点是 10.16.11.91 。
# curl --location --request \
GET 'http://10.16.11.91:6012/sddc/v1/overview/' \
--header 'x-sddc-token: sddc-***'
得到响应如下。
{
"stats": {
"vm_num": 5,
"vm_running_num": 5,
"vm_stopped_num": 0,
"vm_running_cpu_num": 28,
"vm_available_cpu_num": 24,
"vm_running_memory_mb": 20480,
"vm_available_memory_mb": 114688,
"vm_disk_num": 6,
"vm_nic_num": 7,
"vm_image_num": 4,
"vm_snap_num": 1,
"bs_volume_num": 11,
"node_num": 2,
"node_cpu_num": 32,
"node_memory_mb": 131072
}
二、学习和调试工具
查看和调试 OpenAPI 的工具
可以使用 Swagger/Postman/Apifox 工具导入本产品的 OpenAPI Json 文件。
本产品的 OpenAPI/Swagger 数据 URL 地址是:
-
SDDC API:
http://{XHERE管理节点IP地址}/docs/sddc_openapi.json
-
SDS API:
http://{XHERE管理节点IP地址}/docs/sds_openapi.json
活 API 字典
可以在 XHERE 产品中各个资源的审计日志中看到“非 GET 的 API 请求”,可以点击【请求内容】,查看具体 API 请求的参数内容。因此可以通过查看操作路径和请求内容,在情景中快速学习到此 API 请求的具体用法,方便第三方系统对接。
三、API Auth 方式
XHERE 的 API Auth 方式是使用 API Key,需要在 HTTP 请求的 Header 设置,参数名是 x-sddc-token
,token 值可以通过两种方法获取:
-
通过 SSH 登录已安装 XHERE 的主节点,然后使用命令行创建获取 Raw Token。
-
在 XHERE 管理平台中的 Access Token 界面中创建得到,此方法需要 V2.2 版本才支持。
这里介绍下使用命令行方式创建获取 Raw Token,假设 XHERE 集群的主节点是 10.16.11.91,通过 SSH 登录此节点,然后通过 sddc-cli user login -u admin -p {password}
获得 Raw Token。注意 Raw Token 的默认生效时间是 1 小时。
➜ ~ ssh root@10.16.11.91
Last login: Tue Nov 8 00:12:03 2022 from 10.3.0.205
[root@node-91 ~]#
[root@node-91 ~]# sddc-cli user login -u admin -p admin
******
[root@node-91 ~]#
四、API 敏感字段加密
XHERE API 中对敏感字段需要进行加密。对涉及敏感数据的接口调用,需要做如下处理:
- 客户端先调用一个接口获取public key,然后用public key加密敏感数据,最后调用接口,发送加密后的密文。
- 服务端使用public key对应的private key解密获得明文,进行后续业务处理。
以用户登录为例
用户登录操作需要发送2个请求
- 创建RSA key。服务端会生成RSA key(包含一对private key和public key),关联uuid,放入缓存中,有效期5分钟。返回响应中有public key,uuid,expiration。
样例:
POST /sddc/rsa-keys
Response Body
{
"data": {
"rsaKeyCreateOne": {
"expiration": "2022-02-10T14:13:48.165748404+08:00",
"publicKey": "MIGJAoGBAORO/+TrJSnoaR2bXlf4SqYr7EXiUjxnDAl1oOvPaC3RuiB/Fjx+rsAnDWoDS195d6jyd2V//m3KN7m8s+/c6kKtInC8inAjzhRJHlBO0j11bOxIH1ynD/Dn866bBPCGJiY8rWx3+cjnQfbQ+rG9qJFExPzlgJhip5baWYLkh3b/AgMBAAE=",
"uuid": "f494dd2184a14551ab96ed04412edb98",
"__typename": "RsaKey"
}
}
}
- 提交用户登录信息。客户端使用public key加密敏感字段(用户密码),并在请求参数中带上public key对应的uuid。服务端收到请求后,会根据uuid从缓存中找到RSA key,解密敏感字段。进行后续业务处理(此处是验证密码,生成token)。
样例:
POST /sddc/tokens/:login
Request Body
{"data":{"name":"admin","password":"GTEDpzHHbuhvk1uZE8eexuPSeDLb1H52xOhuh7XfaRYEzr5vEuJPIFeAtRAShlL/FCaRuaNIG5AFZLEbOgOKqhdXZGazhqGQ1g/51ZCFwQkfLllxulJpesXfgRbQAXjN/cXkq9J7CctiK07hgwYn1IgsfK/2PSfvGdLZQ+Or8Fc=","rsaKeyUuid":"f494dd2184a14551ab96ed04412edb98"}}
Response Header
set-cookie: X_SDDC_TOKEN=******; max-age=3600; Path=/;
Response Body
{"data":{"tokenLogin":{"data":{"id":4,"name":"","etag":"6c3057ef-54fe-40b4-a222-076b561910bc","uuid":"d8ac595019894363a6f1350f0df80d61","valid":true,"__typename":"TokenData"},"__typename":"Token"}}}
需要加密处理的字段
- UserData.Password
- EmailConfigData.Password
- VirtualMachineSpec.GraphicsPassword
- VirtualMachineSpec.OsUserPassword
需要加密处理的接口
接口名称 | URI | 敏感字段 |
---|---|---|
用户登录 | POST /sddc/tokens/:login | UserData.Password |
创建用户 | POST /sddc/users/ | UserData.Password |
更新用户 | PATCH /sddc/users/{id} | UserData.Password |
验证用户密码 | POST /sddc/users/:verify-password | UserData.Password |
创建email服务器配置 | POST /sddc/email-configs/ | EmailConfigData.Password |
更新email服务器配置 | PATCH /sddc/email-configs/{id} | EmailConfigData.Password |
创建虚拟机 | POST /sddc/virtual-machines/ | VirtualMachineSpec.GraphicsPassword |
VirtualMachineSpec.OsUserPassword | ||
更新虚拟机 | PATCH /sddc/virtual-machines/{id} | VirtualMachineSpec.GraphicsPassword |
Python 程序举例
import requests
import base64
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
api = "http://localhost:6012/sddc/v1"
user_api = f"{api}/users"
rsa_key_api = f"{api}/rsa-keys"
login_api = f"{api}/tokens/:login"
def get_rsa_key():
resp = requests.post(rsa_key_api, headers={"content-type": "application/json"})
return resp.json()
def login(name, password, rsa_key):
uuid = rsa_key["uuid"]
key = base64.b64decode(rsa_key["public_key"])
key = serialization.load_der_public_key(key)
crypted = key.encrypt(password.encode(), padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
encrypted_passwd = base64.b64encode(crypted).decode()
data = {"data": {"name": name, "password": encrypted_passwd, "rsa_key_uuid": uuid}}
resp = requests.post(login_api, json=data)
return resp.json()
rsa_key = get_rsa_key()
token = login("admin", "admin", rsa_key)
print(token["data"]["uuid"])
五、API 设计规范
声明式 API
Kubernetes 流行的一个原因是采用了声明式 API。声明式 API 是一套基于期望状态的系统,用户通过向该系统提出一个期望状态来达成特定的目的。使用声明式 API,用户不需要告诉该系统具体要执行哪些步骤,只需要告诉该系统:我希望哪个资源处于哪种状态。
XHERE 产品从设计开始就采用声明式 API,声明式 API 可以对用户屏蔽系统实现的复杂性,提升 API 的使用体验,有助于实现 IaC。但是,对于一套复杂系统来说,无差别的使用声明式 API 可能会带来不必要的复杂性,因此 XHERE 也在必要的地方使用命令式 API。
ResourceModel
在 SDDC 平台中,我们以 resource 的粒度来管理业务。Resource 可能是虚拟机,存储,网络等硬件资源,也可能是告警,角色等平台所需的软件虚拟资源。SDDC 会对外暴露 REST API 来管理这些资源。
术语
-
Model Layout 是指我们如何为一个 resource 设计它的 model 使用方式。
-
ResourceType 是指 SDDC 中管理的某种资源,例如 VirtualMachine, VmDisk, AlertRule, Alert 等。
-
Resource 则是指单个资源
-
ResourceList 是指某个资源类型的列表
Resource
Resource 是个抽象的概念,包含一个资源的全部信息。Resource 分为两个部分:metadata and data:
-
Metadata:包含可以用于定位这个资源的相关信息,以及可以确定这个资源整体状态的相关信息。这些信息本身都是存在 data 中的某些对象里,只是构建 resource 的过程中被提取出来放到 metadata 中。
-
Data:包含实际存放在数据库中的信息。一个资源的 data 部分可能会存在多个表中,这样的一个表我们称为一个 object。data 部分中的 object 在 resource 中如何摆放,属于下文要提到的 model layout 讨论的范畴。
所以,一个 resource 的结构示例如下:
{
"metadata": {},
"object1": {},
"object2": {}
}
ResourceList
ResourceList 就是由 resource 组成的列表。ResourceList 分为两个部分:metadata and items:
-
Metadata:包含这个列表相关的信息,比如分页信息 Pagination 等。
-
Items:包含这个列表的元素。其中每个元素都是一个 resource。
一个 ResourceList 的结构示例如下:
{
"metadata": {
"pagination": {}
},
"items": [
{},
{}
]
}
两种 Model Layout
在 SDDC 中,一开始就要在资源的数据中表达出资源的当前状态和期望状态。但是,有些资源并不需要区分当前状态和期望状态,例如 alert,事件日志等,这些资源,我们也需要设计一个适合的 model 形式来表达。
这样,我们就有两种 model 表达需求:
-
区分当前状态和期望状态
-
不区分当前状态和期望状态(当前状态 = 期望状态)
不同的表达需求,对应不同的 model 设计形式,这种设计形式,我们称为 layout,即每种表达形式都有自己的 model layout。Model layout 描述的是 Resource 的 data 部分的结构。
第一种 Model Layout: Metadata/Spec/Status
这种类型的 model 分为 3 个部分:
-
Metadata
-
Spec:包含用户对于资源的期望状态
-
Status:包含资源的当前状态
{
"metadata": {},
"spec": {},
"status": {}
}
Get Resource
无论是 GET 一个资源,或者 LIST 多个资源,都是以资源的 Spec 表为准。
Create Resource
当用户要创建一个资源时,提交的是 Spec。
对于新建的资源,Spec.CreationFinish = nil
,表示资源创建还未完成;当资源创建完成后,这个值就会被设置为一个有效的时间。
Update Resource
我们说用户要修改一个资源,指的是 修改这个资源的期望状态,即修改 Spec。
Delete Resource
我们会通过 update-to-deleted 来实现删除操作,避免直接删除。
但是当用户通过 API 发起一个删除请求时,资源是异步删除的(Spec/Stats layout 一定是这样的),所以,在删除完成之前,用户还要能够从 API 里读取到这个资源。我们使用如下实现方案。
-
Spec 表增加两个字段:DeletionBegin 和 DeletionFinish。这两个字段都是时间类型,允许为空,非空表示设置过。
-
API 请求删除一个资源时,设置 Spec.DeletionBegin 字段,表示这个资源开始要被删除。
-
当系统后台执行完资源的实际删除操作后,设置 Spec.DeletionFinish。
-
系统后台发现
Spec.DeletionFinish
已经被设置,则设置 deleted_at 字段的值。这个字段执行过后,API 无法再查询到这个记录,虽然它还在数据库中。
Resource Metadata
Propety | Required | Description |
---|---|---|
id or uuid | yes. 这两个二选一 | 资源 id |
name | 资源名称 | |
created_at | yes | 资源的创建时间 |
creation_finish | 资源创建完成的时间 | |
deletion_begin | 资源被标记为删除的时间 | |
deletion_finish | 资源删除操作执行完成的时间。注意,不是从数据库中删除的时间。 |
状态表达
根据 metadata,可以得到一个资源的状态:
条件 | 状态 |
---|---|
created_at != nil AND creation_finish == nil | 创建中 |
created_at != nil AND creation_finish != nil | 创建完成 |
deletion_begin != nil AND deletion_finish == nil | 删除中 |
deletion_begin != nil AND deletion_finish != nil | 删除操作完成 |
deletion_begin != nil AND deletion_finish != nil | ORM 已删除。 |
Row deleted | Deleted from database |
可能在回收站中
AND deleted_at != nil
Soft deleted, wait GC
第二种 Model Layout: Metadata/Data
这种类型的 model 分为两个部分:
-
Metadata
-
Data:resource 的 data 部分不再进行细分。
-
包含资源的数据表的相关内容。
这种类型的 model 在 API 操作是比较直接的,所有的操作都作用于 data 字段,data 字段只是为了规范 model layout 而增加的一层抽象。
When
这个 layout 适用于最简单的资源表达方式:资源只需要一张数据库表,每个资源对应这个表的一行。
Get Resource
直接调用 model 层封装的 list 接口即可。
无论是 GET 一个资源,或者 LIST 多个资源,都是以资源的 Spec 表为准。
Create Resource
当用户要创建一个资源的时候,提交的是一个 data。
Update Resource
如果是用户提出修改请求,可以通过 PUT,PATCH 等请求提交一个 data。
Delete Resource
通过 API 的 delete 请求实现。
Resource Metadata
每个 resource 都具有如下的元数据:
Propety | Required | Description |
---|---|---|
id or uuid | yes. 这两个二选一 | 资源 id |
name | 资源名称 | |
created_at | yes | 资源的创建时间 |
状态表达
根据 metadata,可以得到一个资源的状态:
条件 | 状态 |
---|---|
created_at != nil | 创建完成 |
deletion_begin != nil AND deletion_finish != nil | ORM 已删除。 |
Row deleted | Deleted from database |
AND deleted_at != nil
Soft deleted, wait GC
六、API 使用规范
我们的 API 是 REST ( Representational state transfer) 风格的,我们设计的 API 在路径、动词的使用、以及返回结果上都需要尽量满足 REST 标准,并在 REST 标准之外提供一些灵活性(这里最主要是指 POST action 这种操作,见下文描述)
API 路径
XHERE 包括两种类型的 API,本文只讨论 SDDC 类型。XHERE API 监听端口是 6012。
-
SDDC API:
http://{XHERE管理节点IP地址}:6012/sddc/v1/
-
SDDC Sample API:
-
SDS API:
http://{XHERE管理节点IP地址:6012/sds/v1/
-
SDS Sample API:
PATH Style
这个 PATH 的完整结构如下:
/sddc/v1/{resource-type}/{uuid|id}[:action-name]?[&queries]
参数解释:
-
resource_type : 我们的资源类型的路径形式,例如 virtual-machiens, vm-disks 等。
-
uuid | id : 资源的标识符。
-
action=action_name : 表示一个 action,具体使用方法见下文。
-
其他 query 参数。
Body Style
API 请求和响应的 body 都是 JSON object。在 Body 形式上,在 SDDC 中,我们将 Resource 或者 ResouceList 的 JSON 格式直接返回。
Requeset Body
API 请求时的 body 包含的内容根据 model layout 的不同而不同,但是都是直接包含对应的 object 对象。与 SDS 的对比如下:
SDS API
// 相关数据会放在一个资源名称(单数)的子 object 下。
POST /sds/v1/osds
{
"osd": {
"disk_id": 1
}
}
SDDC API
以 Spec/Status layout 举例:
// 相关数据直接作为 resource object
POST /sddc/v1/virtual-machines
{
"spec": {}
}
Response Body
响应内容的对比如下:
SDS 的 API
// 获取单个资源,相关数据会放在一个资源名称(单数)的子 object 下。
GET /sds/v1/osds/1/
{
"osd": {
"id"
}
}
// 获取列表,相关数据会放在一个资源名称(复数)的子 object 下,同时还有一个 paging 的子 object 返回分页相关的信息。
GET /sds/v1/osds
{
"osds": [
{
"id": 1
},
{
"id": 2
}
],
"paging": {
}
}
SDDC 的 API
以 Spec/Status layout 举例:
// 获取单个资源,相关数据直接作为返回的 JSON object。
GET /sddc/v1/virtual-machines/{uuid}
{
"metadata": {
"uuid": "uuid1"
},
"spec": {},
"status": {}
}
// 获取列表,会有一个统一的列表数据返回格式作为返回的 JSON object。
GET /sddc/v1/virtual-machines
{
"metadata": {
"pagination": {}
},
"items": [
{
"metadata": {
"uuid": "uuid1"
},
"spec": {},
"status": {]
},
{
"metadata": {
"uuid": "uuid2"
},
"spec": {},
"status": {]
}
]
}
总的来说,API 有两种响应形式:
-
单个资源
-
单个资源主要是返回资源类型所定义的 model 数据,根据 model layout 来返回。
-
多个资源
-
其返回格式是全局定义的,见下文。
多个资源的 Response: ResourceList
响应的 body 定义如下:
{
"metadata": {
"pagination": {}
},
"items": [
{ /* item1 */ },
{ /* item2 */ }
]
}
每个 item 内的数据同单个资源的响应,与单独 get 这个资源时获得的数据一致。
Resource Operations
我们可以对资源或者资源类型发起一个操作,即 operation。operation 是由 method, path, query, req body 形成的一个组合。
Model Layout 1: Metadata, Spec and Status
METHOD | PATH | Query Limit | Req Body Limit | Resp Code | Resp Body | Description |
---|---|---|---|---|---|---|
POST | /sddc/v1/{resource-type} | - metadata (optional) | - 201 | Resource | 创建一个资源。 | |
GET | /sddc/v1/{resource-type}/{uuid, id} | null | - 200 | Resource | 获取一个资源。 | |
GET | /sddc/v1/{resource-type} | - paging parameters (optional) | null | - 200 | ResourceList | 获取多个资源 |
PATCH | /sddc/v1/{resource-type}/{uuid, id} | - metadata (optional) | - 200 如果资源没有修改 | Resource | 修改一个资源的期望状态。只能用于修改 spec 中的标量字段,即普通类型字段,不能是 array 类型或者 object 类型的字段。 | |
DELETE | /sddc/v1/{resource-type}/{uuid, id} | null | - 202 | Resource | 删除一个资源 | |
POST | /sddc/v1/{resource-type}/{uuid, id}:{action} | - 202 | Resource | 对某个资源执行一个操作。 |
no: 禁止
optional: 可选
must: 必选
-
spec (must)
-
status (no)
-
spec (must)
-
status (no)
-
202 如果资源被修改了
Ref: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/
可以用于修改 spec 中的非标量字段,即数组和类型。
如果 action 以 get- 开头,表示这个 API 是只读 API。
Model Layout 2: Metadata and Data
METHOD | PATH | Query Limit | Req Body Limit | Resp Code | Resp Body | Description |
---|---|---|---|---|---|---|
POST | /sddc/v1/{resource-type} | - metadata (optional) | - 201 | Resource | 创建一个资源。 | |
GET | /sddc/v1/{resource-type}/{uuid, id} | null | - 200 | Resource | 获取一个资源。 | |
GET | /sddc/v1/{resource-type} | - paging parameters (optional) | null | - 200 | Resource List | 获取多个资源 |
PATCH | /sddc/v1/{resource-type}/{uuid, id} | - metadata (optional) | - 200 | Resource | 修改一个资源的指定字段。 | |
DELETE | /sddc/v1/{resource-type}/{uuid, id} | null | - 204 | Resource | 获取一个资源 | |
POST | /sddc/v1/{resource-type}/{uuid, id}:{action} | - 202 | Resource | 对某个资源执行一个操作。 |
no: 禁止
optional: 可选
must: 必选
-
data (must)
-
data (must)
如果 action 以 get- 开头,表示这个 API 是只读 API。
分页 Pagination
Offset Based
所有的 List API 都支持此种类型的分页。API 在处理时,会按照如下的顺序处理请求里的 query 参数:
-
字段查询参数。
-
排序参数。
-
分页参数。
Offset based 分页方式是我们产品里提供的一种遍历所有资源的方式。作为一个基础架构的产品,我们需要让用户在必要的时候可以处理所有的资源,所以必须提供一种遍历所有资源的方式。
参数设计
参数 | 位置 | 合法值 | 默认值 | 说明 |
---|---|---|---|---|
offset | query | >= 0 | 0 | |
limit | query | > 0 | 100 |
返回值
分页的返回值存放在 ListMeta 的一个子对象中:
{
"metadata": {
"pagination": {
"offset": {offset},
"limit": {limit},
"count": {count},
"total_count": {total_count}
}
}
}
Example
GET /sddc/v1/compute/virutal-machines?offset=11&limit=10
{
"metadata": {
"pagination": {
"offset": 11,
"limit": 10,
"count": 10,
"total_count": 99
}
},
"items": {
}
}
API 通用查询 & 排序
查询
Venus 针对资源列表 api,实现了通用的查询语法。在 api 查询参数中,通过设置“q”查询参数的值,就可以实现对资源的列表过滤。比如:列出 vm id 为 1 的虚拟机所有的硬盘,示例如下:
GET /sddc/v1/vm-disks/?q=spec.virtual_machine_id:1
查询参数 q 会被解析为一组基本字段条件、逻辑运算符(可选)和括号(可选)组成。
基本查询
基本字段条件包括四部分:字段名称、":"分隔符、比较运算符(可选)和值。
其中,资源的所以字段都有对应的字段名称,包括如下三种类型:
-
data.,比如告警的严重等级,对应的字段名称为 data.severity
-
spec.,比如虚拟机名称,对应的字段名称为 spec.name
-
status.,比如虚拟机的当前运行状态,对应的字段名称为 status.power_state
比较运算符,包括如下七种类型:
-
=,等于(默认值)
-
<>,不等于
-
,大于
-
=,大于等于
-
<,小于
-
<=,小于等于
-
~,正则匹配
比如:列出 size_mb 大于 1024 的所有虚拟机硬盘,示例如下:
GET /sddc/v1/vm-disks/?q=spec.size_mb:>1024
特别的,有时我们需要查询字段为空,或者非空的资源,我们引入了"null" 和 "exists"两个特殊字段名称。
- 过滤出被虚拟机使用中的所有虚拟机硬盘
GET /sddc/v1/vm-disks/?q=exists:status.virtual_machine_id
- 过滤出没被虚拟机使用的所有虚拟机硬盘
GET /sddc/v1/vm-disks/?q=null:status.virtual_machine_id
- 过滤出所有名称以 test 开头的虚拟机。":~"后面可以跟所有常见的正则表达式语法
GET /sddc/v1/virtual-machines/?q=spec.name:~^test
- 过滤出名称包含 test 的虚拟机
GET /sddc/v1/virtual-machines/?q=spec.name:~test
多个值的集合查询
除了基本的比较运算符以外,我们还基于数据库的 IN 查询语法实现了基于多个值的集合查询。注意,集合查询只支持 =(等于)和 <>(不等于)两种运算符。
示例如下,筛选出操作系统名称为 CentOS 7 或者 Centos 8 的镜像。
GET /sddc/v1/images/?q=spec.os_name:("CentOS 7" "CentOS 8")
筛选出操作系统名称不为 CentOS 7 或者 Centos 8 的镜像。
GET /sddc/v1/images/?q=spec.os_name:<>("CentOS 7" "CentOS 8")
如果值中不包含空格字符,可以省略掉引号,不同值之间只用空格分隔。示例如下,筛选出名称为 image-1 或者 image-3 的镜像。
GET /sddc/v1/images/?q=spec.name:(image-1 image-2)
示例,查询 id 为 1 或者 2 的存储策略。
GET /sddc/v1/bs-policys/?q=data.id:(1 2)
SDS 资源,比如 pool 和 osd,多值查询使用 SDS 的语法;SDDC 资源使用上面所示的 SDDC 语法,不要带 OR。
GET /sds/v1/pools/?q=id:(1 OR 2 OR 3)
日期时间查询
对于日期类型,我们目前要求日期格式满足 RFC3339,并通过如下语法实现 range 查询。使用中括号,表示包含边界的 range 查询 [min TO max];使用花括号,表示不包含边界的 range 查询 {min TO max}。* 表示通配符,比如:
- 创建于 2021 年的虚拟机硬盘
GET /sddc/v1/vm-disks/?q=spec.created_at:[2021-01-01T00:00:00.00Z TO 2022-01-01T00:00:00.00Z}
- 创建于 2021 年和之后的虚拟机硬盘
GET /sddc/v1/vm-disks/?q=spec.created_at:[2021-01-01T00:00:00.00Z TO *}
组合字段
除了基本字段条件以外,通用查询还支持通过使用逻辑运算符和括号,对多个基本字段条件进行组合。逻辑运算符目前支持 AND 和 OR 两种。其中 AND 运算符高于 OR 运算符,比如
GET /sddc/v1/vm-disks/?q=size_mb:>1024 OR spec.virtual_machine_id:1 AND status.virtual_machine_id:1
等同于
GET /sddc/v1/vm-disks/?q=size_mb:>1024 OR (spec.virtual_machine_id:1 AND status.virtual_machine_id:1)
值包含空格
如果查询条件中值包含有空格,则需要使用双引号将值扩起来,比如:
GET /sddc/v1/os-releases/?q=data.name:"Windows 7"
GET /sddc/v1/xxxxxx/?q=spec.yyyy:"aaaa bbb ccc"
排序
在 api 查询参数中,通过设置 "sort" 排序参数的值实现对资源列表的排序,值选项如下:
-
data.:desc,data.:desc,...... 降序
-
data.:asc,data.:asc,...... 升序
-
spec.:desc,spec.:desc,...... 降序
-
spec.:asc,spec.:asc,...... 升序
-
data.:desc,data.:asc,...... 混合
-
spec.:desc,spec.:asc,...... 混合
支持多个字段排序,每一个字段排序可选为 desc、asc,如果没有指定,则默认为 desc 即降序。如果 sort 不设置,则查询结果默认以 id desc 排序。
示例 1,列出虚拟机硬盘,根据 id 降序:
/sddc/v1/vm-disks/?sort=spec.id:desc
示例 2,列出节点,根据名称降序:
/sddc/v1/nodes/?sort=spec.name:desc
-
声明式 API
-
SDDC 和 SDS
-
Access Token
七、资源关系
分类 | 中文 | 英文 | 所属功能模块 |
---|---|---|---|
集群 | 数据中心 | DataCenter | 拓扑 |
机房 | Room | 拓扑 | |
机架 | Rack | 拓扑 | |
机框 | Chassis | 拓扑 | |
节点 | Node | 节点 | |
弹性存储 | 物理盘 | Disk | 存储池 |
硬盘 | Osd | 硬盘 | |
存储池 | Pool | 存储池 | |
块存储 | 块存储策略 | BsPolicy | 块存储策略 |
块存储卷 | BsVolume | 块存储卷 | |
卷快照 | BsSnap | 卷快照 | |
网络 | 虚拟交换机 | Vsw | 虚拟交换机 |
网卡 | Nic | 虚拟交换机 | |
桥接网络 | BrNet | 桥接网络 | |
桥接网络命名空间 | BrNetNs | 桥接网络 | |
网桥 | Bridge | 桥接网络 | |
三层网络 | L3Net | 桥接网络 | |
三层网路 IP 区间 | L3NetIpRange | 桥接网络 | |
计算 | 镜像 | VmImage | 镜像 |
虚拟机 | VirtualMachine | 虚拟机 | |
虚拟网卡 | VmNic | 虚拟机 | |
虚拟硬盘 | VmDisk | 虚拟机 | |
虚拟光盘 | VmCdRom | 虚拟机 | |
虚拟机快照 | VmSnap | 虚拟机快照 | |
虚拟网卡快照 | VmNicSnap | 虚拟机快照 | |
虚拟硬盘快照 | VmDiskSnap | 虚拟机快照 | |
虚拟光盘快照 | VmCdRomSnap | 虚拟机快照 | |
虚拟机调度规则 | VmSchedulingRule | 虚拟机 | |
虚拟机迁移任务 | VmMigrationJob | 虚拟机 | |
运维管理 | 告警信息 | Alert | 告警信息 |
告警规则 | AlertRule | 告警规则 | |
审计日志 | AuditLog | 审计日志 | |
告警邮件配置 | EmailConfig | ||
事件 | Event | 事件 | |
监控指标 | Metric | 监控分析 | |
监控视图 | MonitorView | 监控分析 | |
监控数据点 | Sample | 监控分析 | |
告警信息组 | AlertInfoGroup | 告警信息 | |
告警规则组 | AlertRuleGroup | 告警规则 | |
操作日志 | ActionLog |
八、资源状态
XHERE 的资源有两种 Model Layout(参考“二、API 设计规范”)。但是不论哪种 layout,我们都没有一个专门的字段来表示这个资源的当前状态。这主要是因为我们这套系统,资源的状态很复杂,不适合只使用一个字段来表达资源的当前状态了。但对于用户来说,还是需要知道一个资源的当前状态。
Spec/Status Layout 的不一致字段的收敛
我们先来看看 第一种 Model Layout(Metadata/Spec/Status)的字段的特点:字段的收敛。当我们讨论一个字段是否可以收敛时,说明该字段在 Spec 和 Status 中都存在。
当一个字段在 Spec/Status 的值不同时,有如下两种情况:
可收敛字段 | 对于大部分的资源和大部分的字段来说,Spec 和 Status 不一致都是可收敛的。比如虚拟机的 CPU number 这种,系统会在合适的时候进行收敛尝试,直到该差异被收敛为止。 |
---|---|
不可收敛字段 | 某些资源的某些字段,在操作失败后,会处于一个无法继续尝试操作的情况,这种字段,就属于不可收敛的字段,系统不会再进行任何尝试。修复的办法是改变 Spec。 |
对于第二种 Model Layout(Metadata/Data)来说,我们就不用考虑字段差异收敛的问题了。
状态分类
虽然我们不能将资源的状态存储在一个字段中,但是我们还是可以表达出资源的当前状态。
首先,我们将一个资源的状态进行分类。资源的状态分为两类:
-
中间状态
-
稳定状态
什么是中间状态?
不论是哪种 Model Layout 的资源,都含有以下的中间状态:
-
creating:即资源还未完成创建流程。
-
deleting:资源正在进行删除流程。
对于 Metadata/Spec/Status,还增加了如下状态:
- reconciling:资源的 Spec/Status 有差异,正在收敛的过程中。
辅助状态:irreconcilable
对于所有的中间状态,都会包含一个特殊的字段,表示这个资源是否有无法收敛的字段。如果存在无法收敛的字段,那么该资源会一直停在当前状态,知道做出一些操作为止,才有可能发生状态变化。
上述的三个中间状态配合上 irreconcilable 这个辅助状态后,资源的状态解释如下:
-
creating + irreconcilable: 资源创建失败
-
deleting + irreconcilable: 资源删除失败
-
reconciling + irreconcilable: 资源无法收敛
稳定状态
以下状态时是稳定状态:
-
deleted(
deletion_finish != nil
):资源已经完成删除流程。虽然你还能查到该资源,但是它已经不能提供任何服务。 -
active:其他情况下,都属于该状态。
状态如何展示?
状态的展示,基本思路是在 API 的 Metadata 中返回资源的总体状态。如果资源正在发生更新,还要返回有差异的字段的信息。
状态方案设计
API
在 API 响应的 Metadata 中增加两个字段:
-
state:是一个字符串,表示该资源的总体状态,可选值如下:
-
creating
-
active
-
deleting
-
deleted
-
reconciling:表示资源正在更新中。
-
diff_fields: 是一个对象,目前还有两个 key
-
reconciling: 内容是一个字段名组成的列表,表示需要收敛的字段有哪些。
-
irreconcilable: 内容是一个字段名组成的列表,表示有哪些字段是不可收敛的。
-
当这个列表不为空时,表示辅助状态 irreconcilable 为 true。
-
这个列表的字段都会在 reconciling 列表中。
返回值示例如下:
{
"metadata": {
"state": {
"state": "reconciling",
"diff_fields": {
"reconciling": [
"bios",
"core_num_per_socket",
"cpu_mode",
"memory_mb"
],
"irreconcilable": [
"filed"
]
}
}
}
}
前端展示设计
-
资源列表页
-
用独立字段表示 metadata 中的 state,并且可以 hover 展示 diff 的字段信息。
-
可以根据字段需要,单独设计字段的 diff 样式。
-
对于 diff 的字段,优先展示 Status 中的值。
-
资源详情
-
展示 Spec 和 Status 中的所有信息。
九、资源操作举例
集群概览信息获取
方法 | 说明 |
---|---|
GET /overview/ | 物理资源和虚拟资源的数量统计信息 |
GET /settings/ | 集群信息,包括集群名称、集群地址、集群网络段配置、全局 CPU 和内存超分比、全局 VM HA 设置等。 |
GET /license-summary/ | 产品许可信息 |
GET /version/ | 产品版本信息 |
节点信息获取
方法 | 说明 |
---|---|
GET /nodes/ | LIST 节点 |
GET /modes/{id} | 获取节点信息 |
虚拟机信息获取
方法 | 说明 |
---|---|
GET /virtual-machines/ | LIST 虚拟机 |
GET /virtual-machines/{id} | 获取虚拟机信息 |
获取虚拟机信息 API 返回数据如下所示。
{
"metadata": {
"id": 10,
"name": "rongze-test",
"created_at": "2022-10-31T19:17:56.192352+08:00",
"creation_finish": "2022-10-31T19:18:08.033611+08:00",
"project_id": 1,
"state": {
"state": "active",
"diff_fields": {
"reconciling": null,
"irreconcilable": null
}
},
"labels": []
},
"spec": {
"id": 10,
"name": "rongze-test",
"created_at": "2022-10-31T19:17:56.192352+08:00",
"updated_at": "2022-11-03T18:05:47.976022+08:00",
"deleted_at": null,
"creation_finish": "2022-10-31T19:18:08.033611+08:00",
"deletion_begin": null,
"deletion_finish": null,
"etag": "cb1d052e-1d27-4ada-8533-2dafb02ce7fb",
"project_id": 1,
"uuid": "89860d8f-37e2-4efc-945b-cbb8ac14c711",
"description": "",
"time_zone": "UTC",
"arch": "x86_64",
"cpu_num": 4,
"socket_num": 1,
"core_num_per_socket": 4,
"thread_num_per_core": 1,
"cpu_mode": "host-model",
"memory_mb": 4096,
"power_state": "RUNNING",
"ha_enabled": false,
"bios": "Legacy",
"boot_device_order": [
"DISK",
"CDROM",
"NIC"
],
"node_id": 1,
"dest_node_id": null,
"graphics_auth_enabled": false,
"graphics_spice_enabled": false,
"os_type": "Linux",
"os_distribution": "CentOS",
"os_name": "CentOS 7",
"cloud_init_uuid": "146a1131-83aa-46e9-90ed-b086dbc002d9",
"hostname": "",
"os_user_name": "",
"ssh_public_key": "",
"user_data": "",
"hugepage_enabled": false,
"vm_snap_id": null,
"flattened": false,
"rollback_vm_snap_id": null,
"rollback_time": null,
"power_action_type": "start",
"power_action_forced": false,
"power_action_time": "2022-11-01T11:47:58.964162+08:00"
},
"status": {
"id": 10,
"created_at": "2022-10-31T19:17:56.193285+08:00",
"updated_at": "2022-11-03T18:06:05.009698+08:00",
"deleted_at": null,
"etag": "d3e23514-9161-4cc9-8c5b-528ac91b08bd",
"time_zone": "UTC",
"cpu_num": 4,
"socket_num": 1,
"core_num_per_socket": 4,
"thread_num_per_core": 1,
"cpu_mode": "host-model",
"memory_mb": 4096,
"power_state": "RUNNING",
"power_state_reason": "migrated",
"bios": "Legacy",
"boot_device_order": [
"DISK",
"CDROM",
"NIC"
],
"node_id": 1,
"dest_node_id": null,
"vm_migration_job_id": null,
"launched_at": null,
"terminated_at": null,
"graphics_auth_enabled": false,
"graphics_spice_enabled": false,
"last_node_id": 1,
"machine_type": "pc-i440fx-rhel7.6.0",
"graphics_vnc_port": 5900,
"graphics_spice_port": null,
"unschedulable": false,
"unstoppable": false,
"flattened": false,
"rollback_time": null,
"power_action_time": "2022-11-01T11:47:58.964162+08:00",
"storage_fenced": false,
"ip_addresses": [
"10.16.58.3"
],
"vm_snap_num": 0
}
}
查看指定虚拟机下的虚拟硬盘列表
方法 | 说明 |
---|---|
GET /vm-disks/?q=spec.virtual_machine_id:{id} | 查看指定虚拟机下的虚拟硬盘列表 |
查看指定虚拟机下的虚拟硬盘列表 API 返回数据如下所示。
{
"metadata": {
"pagination": {
"offset": 0,
"limit": 100,
"count": 2,
"total_count": 2
}
},
"items": [
{
"metadata": {
"id": 11,
"name": "disk-Bg45d",
"created_at": "2022-10-31T19:17:56.204107+08:00",
"creation_finish": "2022-10-31T19:18:08.020337+08:00",
"project_id": 1,
"state": {
"state": "active",
"diff_fields": {
"reconciling": null,
"irreconcilable": null
}
},
"labels": []
},
"spec": {
"id": 11,
"name": "disk-Bg45d",
"created_at": "2022-10-31T19:17:56.204107+08:00",
"updated_at": "2022-11-03T18:05:47.98081+08:00",
"deleted_at": null,
"creation_finish": "2022-10-31T19:18:08.020337+08:00",
"deletion_begin": null,
"deletion_finish": null,
"etag": "1e6d6531-45fd-4552-a0d0-b07bb46e79b8",
"project_id": 1,
"virtual_machine_id": 10,
"node_id": 1,
"dest_node_id": null,
"bus_type": "Virtio",
"size_mb": 102400,
"storage_type": "rbd",
"bs_policy_id": 2,
"bs_volume_id": 19,
"vm_image_id": null,
"device_index": 1,
"root": false,
"del_vol_on_del": false,
"vhost_enabled": false,
"serial": "129c0eebd18c12f7",
"vm_disk_snap_id": null,
"flattened": false,
"rollback_vm_disk_snap_id": null,
"rollback_time": null
},
"status": {
"id": 11,
"created_at": "2022-10-31T19:17:56.204394+08:00",
"updated_at": "2022-11-03T18:05:48.973948+08:00",
"deleted_at": null,
"etag": "77631dc8-de90-4601-b589-f0dcc131e294",
"size_mb": 102400,
"bs_volume_size_mb": 102400,
"attached": true,
"node_id": 1,
"dest_node_id": null,
"bus_type": "Virtio",
"device_index": 1,
"source_name": "pool-290e9a3e56f545d0969a624661b94c0e/129c0eebd18c12f7",
"vhost_enabled": false,
"flattened": false,
"rollback_time": null,
"device_address": "0000:00:08.0",
"fenced_epoch": null
}
},
{
"metadata": {
"id": 10,
"name": "disk-wFdA3",
"created_at": "2022-10-31T19:17:56.201656+08:00",
"creation_finish": "2022-10-31T19:18:08.02746+08:00",
"project_id": 1,
"state": {
"state": "active",
"diff_fields": {
"reconciling": null,
"irreconcilable": null
}
},
"labels": []
},
"spec": {
"id": 10,
"name": "disk-wFdA3",
"created_at": "2022-10-31T19:17:56.201656+08:00",
"updated_at": "2022-11-03T18:05:47.981856+08:00",
"deleted_at": null,
"creation_finish": "2022-10-31T19:18:08.02746+08:00",
"deletion_begin": null,
"deletion_finish": null,
"etag": "2b4cd4d6-710e-45f0-afd3-d0ff605dfcdf",
"project_id": 1,
"virtual_machine_id": 10,
"node_id": 1,
"dest_node_id": null,
"bus_type": "Virtio",
"size_mb": 102400,
"storage_type": "rbd",
"bs_policy_id": 2,
"bs_volume_id": 18,
"vm_image_id": 6,
"device_index": 0,
"root": true,
"del_vol_on_del": false,
"vhost_enabled": false,
"serial": "117f756ad5123b70",
"vm_disk_snap_id": null,
"flattened": false,
"rollback_vm_disk_snap_id": null,
"rollback_time": null
},
"status": {
"id": 10,
"created_at": "2022-10-31T19:17:56.20222+08:00",
"updated_at": "2022-11-03T18:05:48.976945+08:00",
"deleted_at": null,
"etag": "dcfc98f8-2225-4393-a21b-c9cd0557189f",
"size_mb": 102400,
"bs_volume_size_mb": 102400,
"attached": true,
"node_id": 1,
"dest_node_id": null,
"bus_type": "Virtio",
"device_index": 0,
"source_name": "pool-290e9a3e56f545d0969a624661b94c0e/117f756ad5123b70",
"vhost_enabled": false,
"flattened": false,
"rollback_time": null,
"device_address": "0000:00:07.0",
"fenced_epoch": null
}
}
]
}
块存储策略信息获取
方法 | 说明 |
---|---|
GET /bs-policies/{id} | 获取块存储策略信息 |
获取块存储策略信息 API 返回数据如下所示。
{
"metadata": {
"id": 2,
"name": "bs-policy",
"created_at": "2022-10-31T14:46:27.389292+08:00",
"creation_finish": "2022-10-31T14:46:27.389292+08:00",
"state": {
"state": "active"
},
"labels": []
},
"data": {
"id": 2,
"name": "bs-policy",
"created_at": "2022-10-31T14:46:27.389292+08:00",
"updated_at": "2022-10-31T14:46:27.389292+08:00",
"deleted_at": null,
"creation_finish": "2022-10-31T14:46:27.389292+08:00",
"deletion_begin": null,
"deletion_finish": null,
"etag": "6e29645e-3924-450d-8e33-3ec3801db597",
"description": "",
"storage_type": "rbd",
"sds_pool_id": 3,
"pool_name": "pool-290e9a3e56f545d0969a624661b94c0e",
"max_total_bps": 0,
"burst_total_bps": 0,
"max_total_iops": 0,
"burst_total_iops": 0,
"performance_priority": false,
"crc_check": false,
"bs_volume_num": 11,
"provisioned_size_mb": 798832
}
}
虚拟机镜像上传
虚拟机镜像上传分两个步骤:
- 创建镜像,不提供 URL
- 上传镜像文件
这两个步骤对应的 API 分别如下:
方法 | 说明 |
---|---|
POST /vm-images/ | 创建镜像 |
POST /vm-images/{id}:upload | 上传镜像文件 |
创建上传镜像举例:
curl -H 'x-sddc-token: 651af9ee5aba447cb365a9b75f9dcba2' \
-H 'Content-Type: application/json' \
-X POST -L 'http://127.0.0.1:6012/sddc/v1/vm-images/' -d '
{
"spec": {
"arch": "x86_64",
"bios": "Legacy",
"bootable": true,
"bs_policy_id": 1,
"cloud_config_enabled": true,
"name": "test",
"os_name": "CentOS",
"os_type": "Linux",
"os_distribution": "CentOS",
"size_byte": 619511808,
"type": "vm_disk"
}
}'
上传镜像文件举例:
curl -H 'x-sddc-token: 651af9ee5aba447cb365a9b75f9dcba2' \
-F 'image=@./CentOS-8-GenericCloud-8.4.2105-20210603.0.x86_64.qcow2' \
-X POST -L 'http://127.0.0.1:6012/sddc/v1/vm-images/4:upload'
十、批量操作
在 V2.2 版本会支持 /sddc/v1/batch/
批量操作 API。
十一、监控指标获取
TODO
十二、API 索引
使用 OpenAPI 自动生成。