# Hawkbit DDI API — curl 完整操作手册 基于实际验证的 Hawkbit DDI API v1 交互流程,所有命令均已跑通。 ## 环境信息 ``` Hawkbit Server: http://192.168.142.128:9090 Tenant: DEFAULT Auth: GatewayToken my-gateway-key (DDI 接口) admin:admin (Management 接口) Controller ID: test-curl-001 DistributionSet: id=1 (r3576test:V1.0.0.0) ``` ## 目录 1. [认证配置](#1-认证配置) 2. [自动注册与轮询](#2-自动注册与轮询) 3. [分配 Distribution Set](#3-分配-distribution-set) 4. [拉取部署详情](#4-拉取部署详情) 5. [上报设备属性](#5-上报设备属性) 6. [下载 artifact](#6-下载-artifact) 7. [SHA256 文件校验](#7-sha256-文件校验) 8. [上报执行状态](#8-上报执行状态) 9. [取消任务](#9-取消任务) 10. [确认任务流程](#10-确认任务流程) 11. [查询验证](#11-查询验证) 12. [完整流程速查](#12-完整流程速查) --- ## 1. 认证配置 ### 1.1 开启 GatewayToken 认证(服务端,一次性) ```bash # 设置 token key curl -u admin:admin -X PUT \ -H "Content-Type: application/json" \ "http://192.168.142.128:9090/rest/v1/system/configs/authentication.gatewaytoken.key" \ -d '{"value": "my-gateway-key"}' # 启用 gateway token 认证 curl -u admin:admin -X PUT \ -H "Content-Type: application/json" \ "http://192.168.142.128:9090/rest/v1/system/configs/authentication.gatewaytoken.enabled" \ -d '{"value": true}' ``` ### 1.2 开启 TargetToken 认证(服务端,一次性) ```bash curl -u admin:admin -X PUT \ -H "Content-Type: application/json" \ "http://192.168.142.128:9090/rest/v1/system/configs/authentication.targettoken.enabled" \ -d '{"value": true}' ``` ### 1.3 查看当前所有认证配置 ```bash curl -s -u admin:admin \ "http://192.168.142.128:9090/rest/v1/system/configs" \ | python3 -m json.tool | grep -E "authentication\.(gateway|target)" ``` ### 1.4 认证方式对比 ``` DDI 请求头: GatewayToken → Authorization: GatewayToken my-gateway-key TargetToken → Authorization: TargetToken <设备securityToken> Management API 请求头: Basic Auth → -u admin:admin ``` --- ## 2. 自动注册与轮询 ### 2.1 首次轮询(自动注册) ``` GET /{tenant}/controller/v1/{controllerId} ``` 设备 SN 在 Hawkbit 中不存在时,服务端自动调用 `findOrRegisterTargetIfItDoesNotExist()` 创建 target。 ```bash curl -v \ -H "Authorization: GatewayToken my-gateway-key" \ -H "Accept: application/json" \ "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001" ``` **响应(无待处理 action):** ```json { "config": {"polling": {"sleep": "00:05:00"}}, "_links": { "configData": { "href": "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/configData" } } } ``` **响应(有待处理 action):** ```json { "config": {"polling": {"sleep": "00:05:00"}}, "_links": { "deploymentBase": { "href": "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/deploymentBase/15?c=424529173" }, "configData": { "href": "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/configData" } } } ``` ### 2.2 响应中的 HAL 链接含义 | 链接 rel | 含义 | 下一步动作 | |----------|------|----------| | `deploymentBase` | 有待执行的部署任务 | 拉取部署详情 → 下载 → 安装 | | `cancelAction` | 有取消指令 | 确认取消 | | `confirmationBase` | 有需要确认的任务 | 发送确认 | | `configData` | 服务端要求上报属性 | PUT 属性数据 | | `installedBase` | 有历史安装记录 | 可查询 | --- ## 3. 分配 Distribution Set ``` POST /rest/v1/distributionsets/{distributionSetId}/assignedTargets ``` > ⚠️ 这是 Management API,用 `-u admin:admin` 认证。 ```bash curl -u admin:admin -X POST \ -H "Content-Type: application/json" \ "http://192.168.142.128:9090/rest/v1/distributionsets/1/assignedTargets" \ -d '[{"id": "test-curl-001"}]' ``` **可选参数(完整请求体)**: ```json { "id": "test-curl-001", "type": "forced", "forcetime": 1682408575278, "weight": 1000, "maintenanceWindow": { "schedule": "00:00-04:00", "duration": "02:00:00", "timezone": "+08:00" }, "confirmationRequired": false } ``` **响应**:`200 OK`,返回 `MgmtTargetAssignmentResponseBody`。 分配成功后,设备下次轮询就会拿到 `deploymentBase` 链接。 --- ## 4. 拉取部署详情 ``` GET /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId} ``` ```bash curl -H "Authorization: GatewayToken my-gateway-key" \ -H "Accept: application/json" \ "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/deploymentBase/15" ``` **响应**: ```json { "id": "15", "deployment": { "download": "forced", "update": "forced", "chunks": [ { "part": "os", "version": "V1.0.0.0", "name": "r3576-OS-android", "artifacts": [ { "filename": "update.zip", "hashes": { "sha1": "223377ea691bd7fc47865cccfd802771ff1b3fca", "md5": "28a17f5fb87d637756c0cc5d555d69b7", "sha256": "438b8f9e2af3a3334dc72272d167e0deda8c6e65bb1585202151b3cb866bb530" }, "size": 195411, "_links": { "download-http": { "href": "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/softwaremodules/1/artifacts/update.zip" }, "md5sum-http": { "href": "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/softwaremodules/1/artifacts/update.zip.MD5SUM" } } } ] } ] } } ``` ### 字段说明 | 字段 | 说明 | |------|------| | `id` | action ID,后续反馈操作的关键参数 | | `deployment.download` | `forced`=强制下载 / `attempt`=尝���下载 / `skip`=跳过 | | `deployment.update` | `forced`=强制安装 / `attempt`=尝试安装 / `skip`=跳过 | | `chunks[].part` | 软件模块类型(os / app / …) | | `chunks[].version` | 软件版本号 | | `chunks[].artifacts[].filename` | 要下载的文件名 | | `chunks[].artifacts[].hashes` | SHA1 / MD5 / SHA256 校验值 | | `chunks[].artifacts[].size` | 文件大小(bytes) | | `_links.download-http.href` | **下载地址** | ### download / update handlingType 组合 | download | update | 含义 | |----------|--------|------| | `forced` | `forced` | 立即下载并安装 | | `attempt` | `attempt` | 设备自行决定是否下载/安装 | | `forced` | `skip` | 只下载不安装(download only) | | `skip` | `skip` | maintenance window 未到,等待下次轮询 | --- ## 5. 上报设备属性 ``` PUT /{tenant}/controller/v1/{controllerId}/configData ``` > ⚠️ 必须先轮询一次让 target 自动注册,再上报属性,否则 404。 ```bash curl -H "Authorization: GatewayToken my-gateway-key" \ -H "Content-Type: application/json" -X PUT \ "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/configData" \ -d '{ "mode": "merge", "data": { "osType": "linux", "platform": "x86_64", "kernel": "6.17.0-35-generic", "hostname": "test-curl-001", "hwRevision": "VMware Virtual Platform", "manufacturer": "VMware, Inc.", "swVersion": "V1.0.0.0" } }' ``` ### mode 说明 | mode | 行为 | |------|------| | `merge` | 合并:只更新传入的 key,保留已有属性(推荐) | | `replace` | 替换:完全覆盖已有属性 | | `remove` | 删除:移除传入的 key | **响应**:`200 OK`(空 body) ### 查询设备属性 ```bash curl -u admin:admin \ "http://192.168.142.128:9090/rest/v1/targets/test-curl-001/attributes" ``` --- ## 6. 下载 artifact ``` GET /{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName} ``` ### 6.1 完整下载 ```bash curl -H "Authorization: GatewayToken my-gateway-key" \ -o /tmp/test-update.zip \ "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/softwaremodules/1/artifacts/update.zip" ``` **响应**:`200 OK`,二进制流。 ### 6.2 断点续传(Range 请求) ```bash # 续传:从 byte 1048576 开始 curl -H "Authorization: GatewayToken my-gateway-key" \ -H "Range: bytes=1048576-" \ -o /tmp/test-update.zip.part --append \ "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/softwaremodules/1/artifacts/update.zip" ``` **响应**:`206 Partial Content`,`Content-Range: bytes 1048576-195410/195411`。 ### 6.3 条件请求(If-Match) ```bash curl -H "Authorization: GatewayToken my-gateway-key" \ -H "If-Match: 223377ea691bd7fc47865cccfd802771ff1b3fca" \ -o /tmp/test-update.zip \ "http://..." # SHA1 不匹配 → 412 Precondition Failed ``` ### 6.4 下载 MD5SUM ```bash curl -H "Authorization: GatewayToken my-gateway-key" \ "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/softwaremodules/1/artifacts/update.zip.MD5SUM" ``` **响应**:`28a17f5fb87d637756c0cc5d555d69b7 update.zip` ### 6.5 从 HAL 链接提取下载地址 artifact 的 `_links` 中有两个下载链接,优先级: ``` download → HTTPS(如果有,首选) download-http → HTTP(回退方案) md5sum-http → MD5 校验文件(HTTP) ``` --- ## 7. SHA256 文件校验 ### 7.1 计算本地文件 SHA256 ```bash sha256sum /tmp/test-update.zip ``` ### 7.2 对比部署详情中的哈希值 ```bash # 部署详情中的 SHA256: # "sha256": "438b8f9e2af3a3334dc72272d167e0deda8c6e65bb1585202151b3cb866bb530" echo "438b8f9e2af3a3334dc72272d167e0deda8c6e65bb1585202151b3cb866bb530 /tmp/test-update.zip" | sha256sum -c # → /tmp/test-update.zip: OK ``` --- ## 8. 上报执行状态 ``` POST /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}/feedback ``` ### 8.1 状态上报 JSON 格式 ```json { "timestamp": 1750000000000, "status": { "execution": "download", "result": { "finished": "none", "progress": {"cnt": 2, "of": 5} }, "code": 200, "details": ["Starting download"] } } ``` ### 8.2 四步标准流程 ```bash # 定义变量,方便重复使用 D="http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/deploymentBase/15/feedback" A="Authorization: GatewayToken my-gateway-key" J="Content-Type: application/json" # ① download — 开始下载 curl -H "$A" -H "$J" \ -d '{"status":{"execution":"download","result":{"finished":"none"},"details":["Starting download"]}}' \ "$D" # ② downloaded — 下载完成 curl -H "$A" -H "$J" \ -d '{"status":{"execution":"downloaded","result":{"finished":"none"},"details":["Downloaded update.zip"]}}' \ "$D" # ③ proceeding — 安装中 curl -H "$A" -H "$J" \ -d '{"status":{"execution":"proceeding","result":{"finished":"none"},"details":["Installing update"]}}' \ "$D" # ④ closed — 最终结果 # 成功: curl -H "$A" -H "$J" \ -d '{"status":{"execution":"closed","result":{"finished":"success"},"details":["Installation complete"],"code":200}}' \ "$D" # 失败: curl -H "$A" -H "$J" \ -d '{"status":{"execution":"closed","result":{"finished":"failure"},"details":["Installation failed"],"code":500}}' \ "$D" ``` ### 8.3 execution 枚举值 | execution | Hawkbit Status | 说明 | |-----------|---------------|------| | `download` | `DOWNLOAD` | 开始下载 | | `downloaded` | `DOWNLOADED` | 下载完成 | | `proceeding` | `RUNNING` | 安装进行中 | | `closed` | `FINISHED` / `ERROR` | 最终状态(配合 result.finished) | | `canceled` | `CANCELED` | 已取消 | | `rejected` | `WARNING` | 已拒绝 | | `scheduled` | `RUNNING` | 已计划 | | `resumed` | `RUNNING` | 恢复执行 | ### 8.4 result.finished 枚举值 | finished | 含义 | |----------|------| | `none` | 尚未完成(中间状态) | | `success` | 执行成功 | | `failure` | 执行失败 | ### 8.5 带进度的上报 ```bash # 进度: 第2个/总共5个 artifact curl -H "$A" -H "$J" \ -d '{"status":{"execution":"proceeding","result":{"finished":"none","progress":{"cnt":2,"of":5}},"details":["Downloading artifact 2/5"]}}' \ "$D" ``` ### 8.6 取消反馈(cancelAction feedback) ```bash curl -H "$A" -H "$J" \ -d '{"status":{"execution":"canceled","result":{"finished":"none"},"details":["Cancel acknowledged"]}}' \ "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/cancelAction/15/feedback" ``` ### 8.7 确认反馈(confirmationBase feedback) ```bash curl -H "$A" -H "$J" \ -d '{"confirmation":"confirmed","code":200,"details":["Auto-confirmed"]}' \ "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/confirmationBase/15/feedback" ``` ### 8.8 常见错误 | HTTP 状态码 | 原因 | 解决 | |------------|------|------| | `401` | 认证未通过 | 检查 Authorization header 格式 | | `404` | URL 路径错误或 target 不存在 | 确保 `/{tenant}/controller/v1/...` | | `405` | feedback URL 带了 `?c=xxx` 参数 | POST 前剥离查询参数 | | `410 Gone` | action 已不再活跃 | 已经 closed 或 canceled | | `400 Body Not Readable` | JSON 格式错误 | `"final"` 应为 `"finished"`,`"progress":{}` 应为 `null` 或省略 | --- ## 9. 取消任务 ### 9.1 服务端发起取消(Management API) ```bash curl -u admin:admin -X DELETE \ "http://192.168.142.128:9090/rest/v1/targets/test-curl-001/actions/15" ``` ### 9.2 设备端处理取消 ```bash # ① 轮询拿到 cancelAction curl -H "Authorization: GatewayToken my-gateway-key" \ "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001" # → _links.cancelAction.href # ② 确认取消 curl -H "Authorization: GatewayToken my-gateway-key" \ -H "Content-Type: application/json" \ -d '{"status":{"execution":"canceled","result":{"finished":"none"},"details":["Acknowledged"]}}' \ "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/cancelAction/15/feedback" ``` --- ## 10. 确认任务流程 ### 10.1 开启确认流程(服务端) ```bash curl -u admin:admin -X PUT \ -H "Content-Type: application/json" \ "http://192.168.142.128:9090/rest/v1/system/configs/user.confirmation.flow.enabled" \ -d '{"value": true}' ``` ### 10.2 分配时要求确认 ```bash curl -u admin:admin -X POST \ -H "Content-Type: application/json" \ "http://192.168.142.128:9090/rest/v1/distributionsets/1/assignedTargets" \ -d '[{"id":"test-curl-001","confirmationRequired":true}]' ``` ### 10.3 设备端自动确认 ```bash curl -H "Authorization: GatewayToken my-gateway-key" \ -H "Content-Type: application/json" \ -d '{"confirmation":"confirmed","code":200,"details":["Auto-confirmed"]}' \ "http://192.168.142.128:9090/DEFAULT/controller/v1/test-curl-001/confirmationBase/15/feedback" ``` --- ## 11. 查询验证 ### 11.1 查询 target 基本信息 ```bash curl -u admin:admin \ "http://192.168.142.128:9090/rest/v1/targets/test-curl-001" ``` ### 11.2 查询 target 所有 action ```bash curl -u admin:admin \ "http://192.168.142.128:9090/rest/v1/targets/test-curl-001/actions" ``` ### 11.3 查询指定 action 详情 ```bash curl -u admin:admin \ "http://192.168.142.128:9090/rest/v1/targets/test-curl-001/actions/15" ``` ### 11.4 查询 action 状态历史 ```bash curl -u admin:admin \ "http://192.168.142.128:9090/rest/v1/targets/test-curl-001/actions/15/status" ``` **响应示例(完成全流程后)**: ```json { "content": [ {"id":58, "type": "finished", "messages": ["Installation complete"], ...}, {"id":57, "type": "running", "messages": ["Installing update"], ...}, {"id":56, "type": "downloaded","messages": ["Downloaded update.zip"], ...}, {"id":55, "type": "download", "messages": ["Starting download"], ...}, {"id":54, "type": "download", "messages": ["Target downloads ..."], ...}, {"id":53, "type": "retrieved", "messages": ["Target retrieved ..."], ...}, {"id":52, "type": "running", "messages": ["Assignment initiated ..."]} ], "total": 7, "size": 7 } ``` ### 11.5 查询 target 属性 ```bash curl -u admin:admin \ "http://192.168.142.128:9090/rest/v1/targets/test-curl-001/attributes" ``` ### 11.6 查询所有 target ```bash curl -u admin:admin \ "http://192.168.142.128:9090/rest/v1/targets" ``` ### 11.7 查询所有 Distribution Set ```bash curl -u admin:admin \ "http://192.168.142.128:9090/rest/v1/distributionsets" ``` --- ## 12. 完整流程速查 ### 12.1 一次完整的 OTA 生命周期(复制粘贴即用) ```bash # ═══ 变量设置 ═══ HOST="http://192.168.142.128:9090" TENANT="DEFAULT" CID="test-curl-001" AUTH="Authorization: GatewayToken my-gateway-key" ADMIN="admin:admin" # ═══ Step 1: 轮询(自动注册)═══════════════════════════════════════ curl -s -H "$AUTH" -H "Accept: application/json" \ "$HOST/$TENANT/controller/v1/$CID" | python3 -m json.tool # ═══ Step 2: 分配 Distribution Set(Management API)═════════════════ curl -u $ADMIN -X POST -H "Content-Type: application/json" \ "$HOST/rest/v1/distributionsets/1/assignedTargets" \ -d '[{"id": "'$CID'"}]' # ═══ Step 3: 轮询(拿到 deploymentBase)═════════════════════════════ RESP=$(curl -s -H "$AUTH" -H "Accept: application/json" \ "$HOST/$TENANT/controller/v1/$CID") echo "$RESP" | python3 -m json.tool # 提取 actionId(假设第一次分配,取 deploymentBase URL 中的数字) DEPLOY_URL=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['_links']['deploymentBase']['href'])") AID=$(echo "$DEPLOY_URL" | grep -oP 'deploymentBase/\K\d+') echo "Action ID: $AID" # ═══ Step 4: 拉取部署详情 ══════════════════════════════════════════ DEPLOY=$(curl -s -H "$AUTH" -H "Accept: application/json" \ "$HOST/$TENANT/controller/v1/$CID/deploymentBase/$AID") echo "$DEPLOY" | python3 -m json.tool # 提取下载地址和 SHA256 DL_URL=$(echo "$DEPLOY" | python3 -c " import sys,json art = json.load(sys.stdin)['deployment']['chunks'][0]['artifacts'][0] print(art['_links']['download-http']['href']) ") SHA256=$(echo "$DEPLOY" | python3 -c " import sys,json print(json.load(sys.stdin)['deployment']['chunks'][0]['artifacts'][0]['hashes']['sha256']) ") echo "Download: $DL_URL" echo "SHA256: $SHA256" # ═══ Step 5: 上报属性 ══════════════════════════════════════════════ curl -s -H "$AUTH" -H "Content-Type: application/json" -X PUT \ "$HOST/$TENANT/controller/v1/$CID/configData" \ -d '{"mode":"merge","data":{"osType":"linux","hostname":"'$CID'","swVersion":"V1.0.0.0"}}' # ═══ Step 6: 下载 ══════════════════════════════════════════════════ curl -H "$AUTH" -o /tmp/update.zip "$DL_URL" # ═══ Step 7: 校验 ══════════════════════════════════════════════════ echo "$SHA256 /tmp/update.zip" | sha256sum -c # ═══ Step 8: 上报四步状态 ══════════════════════════════════════════ FB="$HOST/$TENANT/controller/v1/$CID/deploymentBase/$AID/feedback" curl -s -H "$AUTH" -H "Content-Type: application/json" \ -d '{"status":{"execution":"download","result":{"finished":"none"},"details":["Starting"]}}' "$FB" curl -s -H "$AUTH" -H "Content-Type: application/json" \ -d '{"status":{"execution":"downloaded","result":{"finished":"none"},"details":["Downloaded"]}}' "$FB" curl -s -H "$AUTH" -H "Content-Type: application/json" \ -d '{"status":{"execution":"proceeding","result":{"finished":"none"},"details":["Installing"]}}' "$FB" curl -s -H "$AUTH" -H "Content-Type: application/json" \ -d '{"status":{"execution":"closed","result":{"finished":"success"},"details":["Installation complete"],"code":200}}' "$FB" # ═══ Step 9: 验证 ══════════════════════════════════════════════════ curl -s -u $ADMIN "$HOST/rest/v1/targets/$CID/actions/$AID/status" | python3 -m json.tool echo "Done! Action $AID is now finished." ``` ### 12.2 Hawkbit 状态流转图 ``` 管理员分配 DS │ ▼ RUNNING WAITING_FOR_CONFIRMATION │ │ ▼ │ confirmed RETRIEVED ◄─── deploymentBase ▼ │ (轮询返回) RUNNING ▼ │ DOWNLOAD ... │ ▼ DOWNLOADED │ ▼ PROCEEDING ──→ status: RUNNING │ ┌───┴───┐ ▼ ▼ FINISHED ERROR ◄─── closed + success/failure (成功) (失败) 任意阶段 ──cancel──→ CANCELING ──→ CANCELED ``` ### 12.3 API 端点速查表 | 操作 | 方法 | 路径 | 认证 | |------|------|------|------| | 轮询 | GET | `/{tenant}/controller/v1/{controllerId}` | DDI | | 部署详情 | GET | `/{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}` | DDI | | 上报反馈 | POST | `.../deploymentBase/{actionId}/feedback` | DDI | | 取消反馈 | POST | `.../cancelAction/{actionId}/feedback` | DDI | | 确认反馈 | POST | `.../confirmationBase/{actionId}/feedback` | DDI | | 上报属性 | PUT | `/{tenant}/controller/v1/{controllerId}/configData` | DDI | | 下载文件 | GET | `/{tenant}/controller/v1/{controllerId}/softwaremodules/{id}/artifacts/{file}` | DDI | | 下载MD5 | GET | `.../softwaremodules/{id}/artifacts/{file}.MD5SUM` | DDI | | 分配 DS | POST | `/rest/v1/distributionsets/{id}/assignedTargets` | Basic | | 查询 target | GET | `/rest/v1/targets/{controllerId}` | Basic | | 查询 action | GET | `/rest/v1/targets/{controllerId}/actions/{id}` | Basic | | 查询状态 | GET | `/rest/v1/targets/{controllerId}/actions/{id}/status` | Basic | | 查询属性 | GET | `/rest/v1/targets/{controllerId}/attributes` | Basic | | 取消 action | DELETE | `/rest/v1/targets/{controllerId}/actions/{id}` | Basic | | 配置管理 | GET/PUT/DELETE | `/rest/v1/system/configs/{key}` | Basic |