先说说这破事儿是怎么来的
上个月我们搞了个自动化运维脚本,用 Redfish API 批量给新到的 HPE Gen11 服务器做初始配置。结果翻车了。
两台 Gen11,一台 iLO 6 v1.53,一台 iLO 6 v1.66,配置一模一样。脚本跑过去,老的稳如老狗,新的疯狂报 HTTP 400 Bad Request。查了半天文档,Reddit 上也有兄弟在骂这事儿——“Redfish api drives return error” 那个帖子我翻了三遍。
后来发现不是脚本的问题,是 Redfish 规范里的一些坑,加上厂商实现上的微妙差异。今天就把我们踩过的坑、怎么定位的、最终怎么修的,全写出来。
症状描述:你大概率也会遇到
场景一:PATCH 请求改密码,报 HTTP 400
curl -X PATCH \
-H "Content-Type: application/json" \
-H "X-Auth-Token: $TOKEN" \
-d '{"Password": "NewP@ss123"}' \
https://bmc-ip/redfish/v1/AccountService/Accounts/1
返回:
{
"error": {
"code": "Base.1.13.GeneralError",
"message": "A general error has occurred. See ExtendedInfo for more information.",
"@Message.ExtendedInfo": [
{
"MessageId": "Base.1.13.PropertyValueNotInList",
"Message": "The value 'NewP@ss123' for the property 'Password' is not in the list of acceptable values.",
"Severity": "Warning"
}
]
}
}
场景二:GET 请求读网卡信息,报 HTTP 500
curl -s -H "X-Auth-Token: $TOKEN" \
https://bmc-ip/redfish/v1/Chassis/1/NetworkAdapters
返回 500 Internal Server Error,没有任何 ExtendedInfo。这就是最恶心的那种——服务端直接炸了,连个错误码都不给。
场景三:设置一次性启动项为 PXE,报 HTTP 400
这个问题在 HPE ProLiant DL380 Gen10 上特别常见。Reddit 上有人发帖说 redfish_command 模块跑 PATCH 到 Boot 资源时,返回 HTTP Error 400。
根因分析:到底谁在搞鬼
我们把这三个场景拆开看,背后的原因其实就几类:
1. 密码策略冲突(场景一)
这个最坑。Redfish 标准对 Password 属性没有强制校验规则,但 iLO 和 iDRAC 各自有密码复杂度要求。你传一个在标准看来合法的字符串,BMC 的密码策略校验器直接给拒了。
更坑的是错误信息。PropertyValueNotInList 这个 MessageId 意思是"值不在可接受列表中",但你传的是字符串,不是枚举值。这他妈是厂商把错误类型用错了——应该用 PropertyValueFormatError 或者 PropertyValueModified 才对。
2. 固件 BUG 导致 Internal Server Error(场景二)
HTTP 500 在 Redfish 里通常意味着 BMC 内部处理请求时崩了。我们抓包发现,对 /Chassis/1/NetworkAdapters 的 GET 请求,iDRAC 在序列化响应时,某个网卡的健康状态字段是 null,但 JSON 序列化器没做 null 检查,直接抛了 NPE(空指针异常)。
这就是典型的固件质量问题。DMTF 的 Redfish 规范里明确说了——服务端不应该返回 500,应该返回带 ExtendedInfo 的 4xx。但厂商不按规矩来,你能怎么办?
3. Boot 资源属性校验过于严格(场景三)
设置一次性启动项时,你传的 JSON body 里可能包含了 BootSourceOverrideTarget 和 BootSourceOverrideEnabled。问题在于,某些 iLO 固件版本要求这两个属性必须同时出现,且 BootSourceOverrideEnabled 必须先于 BootSourceOverrideTarget 被处理。
如果你只传了 BootSourceOverrideTarget: "Pxe" 没传 BootSourceOverrideEnabled,或者顺序不对,iLO 直接返回 400。
修复步骤:从踩坑到填坑
第一步:开启 Redfish 调试日志(定位问题)
# 在 iDRAC 上启用 Redfish 调试
racadm set iDRAC.Redfish.DebugLevel 2
racadm set iDRAC.Redfish.LogToFile Enabled
# 在 iLO 上
# 通过 SSH 登录 iLO
ssh Administrator@bmc-ip
> set /map1/dump1/flag OemRHD_EnableDebugLogs 1
> set /map1/dump1/flag OemRHD_EnableVerboseLogging 1
然后重现问题,去 BMC 的日志里捞错误堆栈。我们就是在日志里看到了那个 NPE,才确定是固件 BUG。
第二步:修复密码修改的 PATCH 请求
不要直接传明文密码。Redfish 规范里有个更好的做法——使用 ChangePassword 动作(Action),而不是直接 PATCH 属性。
# 正确做法:使用 ChangePassword 动作
curl -X POST \
-H "Content-Type: application/json" \
-H "X-Auth-Token: $TOKEN" \
-d '{
"OldPassword": "oldPassword12",
"NewPassword": "NewP@ss123!"
}' \
https://bmc-ip/redfish/v1/AccountService/Actions/AccountService.ChangePassword
如果厂商不支持 ChangePassword 动作(有些老固件确实没有),那就得先 GET 出密码策略,再构造合法的密码:
# 先获取密码策略
curl -s -H "X-Auth-Token: $TOKEN" \
https://bmc-ip/redfish/v1/AccountService | jq '.Oem'
# 看返回里的 MinPasswordLength、PasswordPattern 等字段
# 然后根据规则生成密码
第三步:绕过 Internal Server Error(场景二)
对于固件 BUG 导致的 500,我们的方案是降级访问路径:
# 不要直接访问 /Chassis/1/NetworkAdapters
# 改走 /Systems/1/NetworkInterfaces
curl -s -H "X-Auth-Token: $TOKEN" \
https://bmc-ip/redfish/v1/Systems/1/NetworkInterfaces | jq '.Members[] | .@odata.id'
或者用 OData 查询参数跳过有问题的属性:
# 只请求需要的字段,避开有问题的序列化路径
curl -s -H "X-Auth-Token: $TOKEN" \
"https://bmc-ip/redfish/v1/Chassis/1/NetworkAdapters?\$select=Id,Name,Status"
我们最终给厂商提了 ticket,两周后新固件修了这个问题。但在那之前,这个 workaround 让我们能继续干活。
第四步:修复一次性启动项设置(场景三)
# 正确做法:同时传两个属性,且按顺序
curl -X PATCH \
-H "Content-Type: application/json" \
-H "X-Auth-Token: $TOKEN" \
-d '{
"BootSourceOverrideEnabled": "Once",
"BootSourceOverrideTarget": "Pxe"
}' \
https://bmc-ip/redfish/v1/Systems/1/BootOptions/1
注意 BootSourceOverrideEnabled 在前,BootSourceOverrideTarget 在后。HPE iLO 对这个顺序敏感,Dell iDRAC 不敏感。这是厂商实现差异。
第五步:统一错误处理逻辑
在脚本里加一个通用的错误重试和降级逻辑:
def redfish_patch(url, payload, token, retries=3):
for attempt in range(retries):
resp = requests.patch(url, json=payload, headers={"X-Auth-Token": token})
if resp.status_code == 200:
return resp.json()
elif resp.status_code == 400:
# 解析 ExtendedInfo
msgs = resp.json().get("error", {}).get("@Message.ExtendedInfo", [])
for msg in msgs:
mid = msg.get("MessageId", "")
if "PropertyValueNotInList" in mid:
# 可能是密码策略问题,尝试获取策略后重试
policy = get_password_policy(url, token)
payload["Password"] = generate_valid_password(policy)
elif "ActionNotSupported" in mid:
# 降级到 PATCH 属性
return fallback_patch(url, payload, token)
elif resp.status_code == 500:
# 固件 BUG,等待后重试或走备用路径
time.sleep(5)
url = find_alternative_endpoint(url)
raise Exception("Redfish API 重试耗尽")
各厂商 Redfish 实现对比
| 特性 | HPE iLO 6 | Dell iDRAC 9 | Lenovo XClarity |
|---|---|---|---|
| PATCH 密码支持 | ✅ 支持 ChangePassword 动作 | ✅ 支持直接 PATCH | ✅ 支持 ChangePassword 动作 |
| 密码策略暴露 | 通过 Oem.Hpe.PasswordRules | 通过 Oem.Dell.DellPasswordService | 通过 Oem.Lenovo.PasswordPolicy |
| Boot 属性顺序敏感 | ✅ 是 | ❌ 否 | ❌ 否 |
| 500 错误频率 | 低(固件 1.60+) | 中(特定端点) | 低 |
| ExtendedInfo 完整性 | 高 | 中(有时缺失) | 高 |
| 认证方式 | Session + Token | Session + Token + Basic | Session + Token |
FAQ
什么是 API 错误码?
HTTP 状态码。200 表示成功,4xx 是客户端问题(你传的数据不对),5xx 是服务端问题(BMC 炸了)。Redfish 的扩展信息在 @Message.ExtendedInfo 里,比裸 HTTP 状态码有用得多。
Redfish API 是什么?
DMTF 定义的管理接口标准。用 RESTful HTTP + JSON,替代了老掉牙的 IPMI。支持从服务器到机架级管理。说白了就是 BMC 的 REST API。
什么情况下会返回 500 Internal Server Error?
通常是 BMC 固件在处理请求时崩溃了。比如 JSON 序列化时遇到 null 字段没做保护,或者内部状态机走到了未预期的分支。我们的经验是——先升级固件到最新版,能解决 80% 的 500 错误。
如何启用 Redfish 服务?
- iDRAC: 网页进
Overview > iDRAC Settings > Network > Services,勾选 Redfish 启用。 - iLO: 网页进
Administration > Access Settings > Services,启用 Redfish REST API。 - 命令行:
racadm set iDRAC.Redfish.Enable 1(Dell)或ssh iLO执行set /map1/config/RedfishEnabled 1(HPE)。
最后说两句
Redfish 是个好协议,但厂商实现参差不齐。我们踩的这些坑,说到底都是固件质量和规范遵守程度的问题。
如果你也在用 Redfish 做自动化,我建议:
- 永远先升级 BMC 固件到最新版。我们遇到的 500 错误在新固件里修了。
- 别信厂商的文档——抓包看实际交互才是真理。
- 对每个厂商做单独的适配层。一劳永逸的通用客户端?不存在的。
Reddit 上那个问 “Redfish api drives return error” 的哥们,最后也是升级固件解决的。所以第一件事永远是:升级固件。