ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Redfish前置课:搞懂RESTful API与JSON,才能玩转服务器带外管理

Redfish前置课:搞懂RESTful API与JSON,才能玩转服务器带外管理 在做服务器硬件管理、带外运维或者基础设施自动化的朋友应该都听过Redfish这个名字。但真到了上手阶段很多人不是被Redfish本身的接口复杂到劝退而是被前置知识卡住RESTful API的规范到底怎么理解JSON的响应体又该怎么读怎么解析。Redfish这套接口标准本质上就是“跑在HTTP上的RESTful API”所有交互报文都用JSON承载。所以我把这篇定位成Redfish前置学习把RESTful API和JSON这两块地基彻底讲明白再配合一套能直接照着敲的实操流程让你在碰真正的Redfish设备之前先把底层逻辑吃透。这篇内容适合谁准备做BMC带外管理脚本的运维工程师、刚接触数据中心硬件自动化的开发以及那些已经在读Redfish官方文档但被POST /redfish/v1/Systems/{id}/Actions/ComputerSystem.Reset之类接口绕晕的人。看完你会清楚Redfish为什么选RESTURI和JSON字段该怎么对应以及遇到解析报错时怎么从HTTP和JSON两个层面去排查。1. Redfish为什么偏偏选了REST和JSON先把“新管理协议”的账算清楚1.1 从IPMI到Redfish管理接口面临的代际问题在Redfish出来之前服务器带外管理事实上的标准是IPMI。IPMI本身不是不能干活它的问题是命令式协议走的是RMCP/UDP这种底层通道发送的是二进制命令。你给服务器发一条“读取CPU温度”的指令服务器返回一串字节码要读懂得靠专门的工具或者驱动。这种设计在单机维护时代够用但放到云数据中心里就非常别扭你没法用常见的HTTP客户端去调也没法把管理数据和公司的监控平台直接打通更别提在脚本里优雅地处理返回结果。还有一点很致命IPMI对网络模型的依赖太重跨三层网络、穿防火墙都要专门配置。现在的数据中心管理越来越强调自动化、可编程、可观测运维体系里到处是RESTful API和JSON数据交换IPMI那套字节流协议就像老式电话交换机功能还在但已经跟不上现代IT的调度方式。DMTF组织推出Redfish的时候思路很明确干脆把管理协议做成“现代Web应用的样子”。你可以把Redfish理解成一套专门给服务器硬件用的HTTP接口规范数据表示用JSON资源组织用URI操作方式遵循RESTful风格。于是浏览器插件、curl脚本、Python脚本甚至Prometheus的exporter都能直接跟BMC对话。1.2 REST和JSON在Redfish里到底承担了什么角色RESTRepresentational State Transfer不是一个具体的软件而是一组架构约束。它要求你把业务能力抽象成“资源”用HTTP方法去表达对资源的操作并且通信状态通过标准的状态码来传递。Redfish就是严格按这套思想设计的服务器是资源电源是资源散热风扇是资源固件版本也是资源。你要做的是对这些资源进行读、改、删、执行操作而不是像传统API那样发一堆“命令动词”。JSON在这里的作用更直接。Redfish的每个响应都是一个JSON文档里面包括资源属性、链接关系、状态信息等。比如你GET一个系统资源返回的Body就是一段JSON里面有Status里包含State和Health有Boot对象描述启动配置有Links指向关联的其他资源。你看整个管理界面从“命令字节流”变成了“资源结构化文本”人可读程序也好解析。做个粗略对比你就能感受到差别维度IPMIRedfish传输方式RMCP/UDP二进制命令HTTP/HTTPS数据格式字节流需专门解码JSON文本直接可读调用方式专用命令工具任何HTTP客户端curl都行扩展性厂商私有命令多难统一标准资源模型OData扩展自动化友好度很低很高天生配合Ansible、Python所以Redfish选REST和JSON不是拍脑袋而是管理协议从“设备时代”进入“软件时代”的必然结果。2. RESTful API的三个核心概念对照Redfish具体接口理解2.1 资源和URIRedfish的地址不是“命令”是“物品”RESTful API里最重要的思维转变是你面对的不再是一堆函数而是一堆“东西”。Redfish里的每个“东西”都有唯一地址也就是URI。比如https://bmc-ip/redfish/v1/是Redfish服务的入口https://bmc-ip/redfish/v1/Systems/是服务器系统的集合https://bmc-ip/redfish/v1/Systems/437XR1138R2/是某台具体的服务器/redfish/v1/Chassis/1U/是机箱资源/redfish/v1/Managers/BMC/是BMC自身的管理资源注意这里的命名风格路径里全是名词没有动词。你不会看到GetSystemStatus这种地址只会看到Systems/{id}这样的资源路径。这个设计看似简单实际影响很大因为资源是嵌套的你顺着一个链接就能发现其他相关资源。在Redfish的JSON响应里几乎每个对象都会带一个odata.id字段它的值就是该资源的URI。这就是REST里HATEOAS思想的体现——服务器在返回数据的同时告诉你“这个资源的相关入口在哪里”。我见过不少新手拿着文档里的URI硬记其实根本不用背你只要GET一次资源看它返回的Links和odata.id整个资源地图就出来了。2.2 四个动词GET读、POST建、PATCH改、DELETE删RESTful API对资源的操作靠HTTP方法也叫动词。Redfish用得最多的也就四个GET读取资源。不改变状态是幂等的随时可以反复调用。POST创建资源或者触发一个动作。Redfish里很多动作都挂在POST上比如重启、开关机、更新固件。PATCH局部更新资源。只改你传上去的字段其他字段不动。Redfish里调整启动顺序、修改资产信息基本都是PATCH。DELETE删除资源。Redfish里偶尔用到比如删除虚拟介质挂载、删除Session。PUT很少见因为PUT是整量替换动不动就把整个对象覆盖掉对硬件管理来说风险太大。Redfish官方设计也刻意回避了PUT更推荐PATCH这种增量式修改。这点你在调接口时要注意别想着一口气PUT整个服务器配置上去大概率会被拒绝或者引发副作用。举个例子如果你想设置某台服务器下一次从PXE启动curl -k -X PATCH https://bmc-ip/redfish/v1/Systems/437XR1138R2/ \ -H Content-Type: application/json \ -d {Boot:{BootSourceOverrideTarget:Pxe,BootSourceOverrideEnabled:Once}}这里就是把Boot这个嵌套对象里的两个字段改掉其他启动配置原样保留。这种“只动局部”的方式正是PATCH在Redfish里被广泛使用的理由。2.3 状态码与统一错误体Redfish告诉你“错在哪一层”RESTful API必然依赖HTTP状态码来表达结果Redfish也不例外。你基本会遇到这些200 OKGET成功或者POST动作执行成功。201 Created资源创建成功比如创建了Session。204 No Content操作成功但没有返回体比如某些DELETE。400 Bad Request请求格式有问题多半是JSON语法错或者字段值不合法。401 Unauthorized没认证token丢了或者账号密码错。403 Forbidden认证通过但没权限。404 Not FoundURI不存在或者资源ID写错。405 Method Not Allowed方法用错比如把POST用在了只支持GET的资源上。500 Internal Server ErrorBMC内部处理出错。Redfish还规定了统一的错误响应结构。一个典型的错误体长这样{ error: { code: Base.1.8.ResourceNotFound, message: The requested resource was not found., Message.ExtendedInfo: [ { odata.type: #Message.v1_1_2.Message, MessageId: Base.1.8.ResourceNotFound, Message: The requested resource was not found., Resolution: Check the URI and try again. } ] } }很多人第一次看到code字段里的Base.1.8.ResourceNotFound会觉得奇怪这不是随便起的名字它是Redfish消息注册表里定义的标准化消息ID。Base是消息注册表名1.8是版本ResourceNotFound是具体消息名。拿到这个code你就能去官方消息注册表里查标准含义而不是盯着厂商翻译过度的中文提示猜。实际排查中状态码决定排查方向code决定具体原因两者配合用能少走很多弯路。3. JSON基础补课读懂Redfish响应之前必须避开的坑3.1 JSON的6种数据类型与Redfish常见组合JSON的数据类型不多就六种对象{}、数组[]、字符串...、数字123、1.5、布尔值true/false、空值null。Redfish的响应基本就是这几个类型的排列组合。给你看一个Redfish里常见的系统资源片段{ odata.id: /redfish/v1/Systems/437XR1138R2/, odata.type: #ComputerSystem.v1_16_0.ComputerSystem, Id: 437XR1138R2, Name: Web Front End Node, SystemType: Physical, AssetTag: Chicago-1A, Manufacturer: Contoso, Model: 3500RX, SerialNumber: 437XR1138R2, MemorySummary: { TotalSystemMemoryGiB: 64, Status: { State: Enabled, Health: OK } }, Boot: { BootSourceOverrideTarget: None, BootSourceOverrideMode: UEFI }, Links: { Chassis: [ { odata.id: /redfish/v1/Chassis/1U/ } ], ManagedBy: [ { odata.id: /redfish/v1/Managers/BMC/ } ] } }你看MemorySummary是对象里面的Status又是嵌套对象Links里的Chassis是数组数组元素又是对象。这种复合结构在Redfish里太普遍了所以解析JSON不能只看一层要习惯“点路径”式地往下走比如data[MemorySummary][Status][State]。3.2 数组与对象定位一个硬盘、一条日志的方法很多人解析JSON容易把数组和对象搞混在Redfish场景里这个错误代价很大。拿存储来说你GET/redfish/v1/Systems/{id}/Storage/返回的通常是{ odata.id: /redfish/v1/Systems/437XR1138R2/Storage/, Members: [ { odata.id: /redfish/v1/Systems/437XR1138R2/Storage/SATA-1/ }, { odata.id: /redfish/v1/Systems/437XR1138R2/Storage/NVMe-1/ } ], Membersodata.count: 2 }Members就是数组它表示“集合下的成员列表”。你要遍历所有存储控制器就得用循环storage_members data.get(Members, []) for member in storage_members: storage_uri member[odata.id] print(storage_uri)注意Membersodata.count这个字段它告诉你集合里有多少成员。这是Redfish里OData分页约定的体现当你资源数量多时响应可能不会返回全部成员而是分页返回你要靠这个字段判断是不是还有下一页。不少人写脚本死盯着第一页数据结果漏掉了一半服务器原因就是没处理Membersodata.count和NextLink。日志场景也一样。你GET日志服务集合返回的Entries字段是数组数组每个元素是一条日志。凡是看到字段名带复数、值以[开头的基本就是数组必须用索引或循环访问不能直接点属性名。这条规律在不同厂商的Redfish实现里都通用。3.3 “JSON没毛病代码报错”的典型场景强类型反序列化JSON本身很宽容但很多编程语言是强类型的把JSON“翻译”成对象的时候就会爆发各种奇葩报错。拿热搜里常见的java.util.Date反序列化错误来说典型提示是JSON parse error: Cannot deserialize value of type java.util.Date from String 2024-08-21T10:00:00Z: not a valid representation这个错不怪JSONJSON字符串2024-08-21T10:00:00Z本身挺标准是某个实现没告诉Jackson怎么把这个字符串转成java.util.Date。解决方案通常是在Java类上加上JsonFormat注解JsonFormat(shape JsonFormat.Shape.STRING, pattern yyyy-MM-ddTHH:mm:ssZ, timezone UTC) private Date timestamp;这种问题在Redfish里特别常见因为Redfish大量使用带时区的UTC时间字符串比如2024-08-21T10:00:00Z、2024-08-21T10:00:0008:00。你用Java SDK解析Redfish时间字段十有八九会遇到日期格式不对的问题。学到这一课以后再看到反序列化报错先别怀疑响应体先去看你代码里的类型定义跟JSON字段对不对得上。我做了一个表格把Redfish场景下最常碰到的几类解析问题列出来报错方向常见原因排查顺序无法反序列化字符串为DateJSON时间格式与语言默认格式不匹配先看原始JSON值再检查时间解析格式期望对象但得到数组字段名理解错比如把Members当对象打印响应体看实际是什么结构键名不存在不同厂商Redfish实现字段有差异用jq或Python的keys()先看所有键嵌套过深无法自动映射响应结构里有Links这类递归引用自己写解析不要全指望自动映射中文乱码响应体编码不匹配确保HTTP客户端按UTF-8解码4. 实操用curl与一台“假Redfish”对话把整套流程跑通4.1 没有真机怎么办用mockup仿真器搭一套练习环境学Redfish最大的阻碍是没设备。升级一次固件要审批重启一台服务器可能影响业务谁也不敢在真机上乱试。好在Redfish有一套官方mockup也就是标准示例数据配合DMTF提供的mockup服务器脚本可以在本机跑一个仿真的Redfish服务。做法很简单先找一个Redfish mockup数据包再用Python的redfish-mockup-server脚本把它服务化。大致步骤是# 下载mockup数据后进入目录 python3 redfish-mockup-server.py -host 0.0.0.0 -port 8000 /path/to/mockup/folder然后本机就多了一个跑在8000端口的Redfish服务随你怎么折腾。没有图形界面的服务器环境也行只要Python3能跑起来就行。这里有个细节要留意mockup服务器默认是只读的你执行PATCH操作它会返回“操作不允许”但它依然会完整展示RESTful API的资源和结构。练手的目标本来就不是改真实状态而是熟悉URI、响应体、认证流程这点足够了。如果你有真实的BMC环境那就更方便直接用BMC的IP地址替换即可。但建议第一步还是在mockup上做至少你可以放心地把每个接口都GET一遍看真实的JSON长什么样。4.2 认证那条路Basic Auth与Session Token都要会Redfish的认证有两种常见形态手动调接口时你最好两种都掌握。第一种是Basic Auth就是HTTP自带的用户名密码认证curl -k -u admin:password https://bmc-ip/redfish/v1/Systems/-k是忽略证书校验因为BMC的HTTPS证书通常是自签名的。-u把账号密码塞进请求头。这种方式简单直接但每次请求都要携带凭据安全性不如Session。第二种是创建Session先认证一次拿到token后续请求用token# 1. 创建Session返回的Header里有X-Auth-Token curl -k -i -X POST https://bmc-ip/redfish/v1/SessionService/Sessions \ -H Content-Type: application/json \ -d {UserName:admin,Password:password}注意这里我加了-i目的是查看响应头。因为X-Auth-Token不在响应Body里而在Header里。你用-i会看到类似HTTP/1.1 201 Created Location: /redfish/v1/SessionService/Sessions/12345/ X-Auth-Token: abcdef0123456789把token复制出来后面的请求都用它curl -k -H X-Auth-Token: abcdef0123456789 \ https://bmc-ip/redfish/v1/Systems/Session的好处是用完可以DELETE掉这个Session资源主动让token失效比长期裸奔的Basic Auth安全。练习阶段两个都要跑一遍因为不同厂商BMC对认证的支持差别还挺大有的默认关闭Session服务有的只允许Basic。会两套方案你到任何环境都不慌。4.3 三个高频操作查系统、改启动项、发重置命令在仿真环境跑通认证后我建议你依次做三个高频操作这三个操作几乎覆盖了Redfish日常使用的七成场景。操作一查看系统列表和关键信息curl -k -H X-Auth-Token: $TOKEN \ https://bmc-ip/redfish/v1/Systems/拿到返回的JSON后先别急着写脚本用Python或者jq格式化一下把Members数组里的odata.id都列出来。然后选一个系统ID再GET详情curl -k -H X-Auth-Token: $TOKEN \ https://bmc-ip/redfish/v1/Systems/437XR1138R2/重点关注PowerState当前电源状态、Status健康状态、MemorySummary内存信息、ProcessorSummaryCPU信息这几个字段。这些是巡检脚本里会出现频率最高的属性。操作二设置一次性PXE启动curl -k -X PATCH https://bmc-ip/redfish/v1/Systems/437XR1138R2/ \ -H Content-Type: application/json \ -H X-Auth-Token: $TOKEN \ -d {Boot:{BootSourceOverrideTarget:Pxe,BootSourceOverrideEnabled:Once}}这里的“Once”很关键它表示只在下一次开机时覆盖启动源之后再恢复正常启动顺序。这比直接改默认启动顺序安全得多免得服务器以后每次开机都去网卡找PXE半天起不来系统。操作三远程重启服务器curl -k -X POST https://bmc-ip/redfish/v1/Systems/437XR1138R2/Actions/ComputerSystem.Reset \ -H Content-Type: application/json \ -H X-Auth-Token: $TOKEN \ -d {ResetType:ForceRestart}ComputerSystem.Reset是Redfish标准动作路径固定跟在系统资源的Actions下面。ResetType支持的值很多像On、ForceOff、GracefulRestart、ForceRestart、Nmi不同BMC实现支持的范围不太一样可以先OPTIONS一下看看允许哪些值。很多厂商文档不会逐字列全但你可以通过GET系统资源的Actions字段看到这个系统具体支持哪些ResetType。这三个操作跑一遍你对“资源HTTP方法JSON请求体”这套Redfish交互模式就有手感了。5. 用Python写第一个Redfish巡检脚本附关键细节5.1 为什么学习阶段不建议直接上官方SDK一搜Redfish Python很容易找到官方提供的redfish库封装了session、请求、解析等一堆东西。我的建议是学习阶段先别急着用它直接拿requests写。理由很简单官方SDK帮你隐藏了太多细节。你不知道token怎么传的不知道请求超时怎么处理的不知道返回数据在哪个对象里。一旦到了某些厂商BMC实现有差异的环境SDK报错你会完全无从排查。先用requests裸写一遍你会深刻理解认证头、超时、状态码、JSON解析这些基本功以后再上SDK就能看懂它在干什么。5.2 封装请求、处理分页和缺失键一个基础巡检脚本大致长这样import requests import json BMC_IP 192.168.1.100 USERNAME admin PASSWORD password BASE_URL fhttps://{BMC_IP}/redfish/v1 s requests.Session() s.auth (USERNAME, PASSWORD) s.verify False # 忽略自签名证书 def get_resource(path): url f{BASE_URL}/{path} resp s.get(url, timeout10) resp.raise_for_status() return resp.json() # 获取系统列表 data get_resource(Systems/) for member in data.get(Members, []): system_uri member[odata.id] # 去掉前缀直接用相对路径 relative system_uri.replace(BASE_URL, ).strip(/) detail get_resource(relative) print(json.dumps({ id: detail.get(Id), power_state: detail.get(PowerState), health: detail.get(Status, {}).get(Health), }, indent2))这里有几个关键细节我特别提醒一下。第一s.verify False在真实生产环境别这么写但练习阶段不关掉你大概率会被自签名证书卡死。更规范的做法是用verify/path/to/ca.pem指定CA证书。第二timeout10必须加。BMC有些操作响应很慢不加超时脚本会无限卡住尤其是批量巡检几十台机器时一台不响应就拖垮整个任务。第三取嵌套字段务必用.get(key, {})的链式写法不要直接detail[Status][Health]。因为不同厂商的BMC可能不返回Status或者Status是空的一旦键不存在你的巡检任务就中断了。用get加默认值脚本的容错性会好很多。5.3 对“看起来正常但运行报错”的三个排查顺序写Redfish脚本最气人的一种情况是curl一敲就出数据Python脚本一跑就报错看起来无比诡异。我的排查顺序永远是这三步。第一步打印原始响应文本。很多人在resp.json()这一步抛异常不代表服务端返回的不是JSON而是响应里混入了非JSON内容。比如登录失败的页面返回的是HTML或者网络设备在JSON前面加了个BOM头。先print(resp.text)看真实内容比自己瞎猜强百倍。第二步检查请求头。Redfish很多接口要求Content-Type: application/json很多接口对Accept头也有要求。你用curl时可能带了某些默认头脚本里没注意就漏了服务端行为会完全不同。把Python的请求头跟curl的-v输出对比基本能找到差异。第三步单独验证响应里的某个字段。如果数据能解析但字段拿不到就用Python交互环境或者jq手动检查这个结构的真实键名。我之前就遇到过厂商把MemorySummary改成了Memory的兼容实现文档没更新导致脚本读出None。这种事在Redfish生态里不少见所以写解析逻辑前先用脚本把待解析的JSON打得漂漂亮亮看清键名再动手。6. 从热搜问题看JSON实战中最容易卡住的三类场景6.1 格式化、校验与转换工具怎么用才不至于帮倒忙网上搜JSON跳出来的高频问题一大半是关于格式化、校验、转换的。Redfish场景里格式化工具确实有用尤其是响应数据一长串压成一行时不格式化根本没法看。我习惯的做法是把响应体存成resp.json文件然后在命令行用jq处理curl -k -H X-Auth-Token: $TOKEN \ https://bmc-ip/redfish/v1/Systems/437XR1138R2/ -o system.json jq . system.jsonjq .就是漂亮的格式化输出。如果你想提取某个字段直接写路径jq .Status.Health system.json至于在线格式化工具也不是不能用但注意别把真实BMC的敏感信息贴进去。我见过有人图省事把带SerialNumber和MAC地址的Redfish响应贴到公开网页上这在企业环境是妥妥的信息泄露风险。本地用jq或者VS Code插件效果一样还能保住隐私。关于JSON转CSV、转Excel这类需求Redfish场景其实不太建议做通用转换。因为Redfish嵌套结构太多展平后字段含义容易丢失。真要导数据做报表我建议按需自己写Python脚本明确选择要导出的字段而不是指望一个万能转换器。6.2 JSON对象和JSON数组被搞混时的定位方法你看热搜里有不少“json数组”“json对象”的疑问Redfish里这个坑尤其高频。简单判断方法就三条以{开头是对象表示一个实体的属性集合。以[开头是数组表示一组实体。数组中每个元素可以是对象Redfish里最常见的就是Members数组里面每个元素都有odata.id。很多人写解析代码时报“对象不能用下标遍历”基本就是把数组当对象用了。我的习惯是第一次解析一个从未见过的Redfish资源前先用type()或者jq type确认顶层结构data resp.json() print(type(data)) # class dict 或 class list print(type(data[Members])) # 一定是 list这种确认看起来多此一举但能省下大量瞎试的时间。尤其是你对接的厂商BMC版本比较旧字段结构跟最新标准不一致一上来就硬写解析代码报错之后还要回头猜结构效率极差。先确认结构再写路径是JSON解析的黄金法则。6.3 不同语言处理JSON的脾气差异Python、Java、Shell最后说说不同语言对JSON的“脾气”这也是热搜里跨语言JSON问题特别多的原因。Python的json模块把JSON转成字典和列表跟JavaScript一样天然贴合。你只需要注意true和false会变成Python的True和FalseJSON里的null会变成None别在代码里直接比较字符串就行。Java相对麻烦。你拿到的是JsonObject、JsonArray或者是项目里定义的POJO配合Jackson、Gson反序列化。Java的强类型要求每个字段都有对应的类定义JSON里多一个字段或者少一个字段或者日期格式不对反序列化就可能炸。解决思路是灵活使用JsonNode这类树模型树模型可以避免为每个接口都写一大堆类适合Redfish这种结构经常轻微变化的场景。Shell这边我强烈建议用jq而不是用grep和sed去抠JSON。jq是专门为JSON设计的支持条件、管道、切片比如jq -r .Members[] | .[odata.id] system.json这行命令会输出所有成员的URI。如果用grep去抠遇到字符串里恰好包含odata.id的干扰项就会出错。Redfish场景里短平快的运维脚本用jq复杂巡检任务用Python这个分工我一直觉得最顺手。写Redfish脚本做得多了我自己最大的体会是这个领域90%的问题都出在“没有把RESTful API和JSON这两层基础当真”。很多人一上来就背接口、套SDK遇到响应结构变化就蒙。其实只要把资源、URI、状态码、JSON嵌套结构这四件事搞清楚再配合curl和Python反复试Redfish的学习曲线会平滑很多。如果你非要从一个动作开始练我建议先把mockup环境跑起来从一次GET请求开始然后逐步加认证、加PATCH、加动作调用一步步把REST和JSON的感觉建立起来后面看任何厂商的Redfish文档都会轻松得多。
返回列表