ServerBox 的 BMC(Redfish)支持详解:带外电源管理与传感器读取的设计与硬件兼容性
【免费下载链接】flutter_server_boxServerBox - server status & toolbox项目地址: https://gitcode.com/GitHub_Trending/fl/flutter_server_box
Server Box 通过 Redfish 的 HTTPS/JSON 接口与主板上的 BMC(基板管理控制器)通信,在主机断电、卡死或重启时仍能读取电源状态与硬件传感器、执行电源操作,作为 SSH 与 Monitor agent 之外的独立带外管理通道。本文以 docs/src/content/docs/zh/principles/bmc.md 为核心,结合 BmcCfg / BmcCredential 模型 与 BmcNotifier 轮询实现,完整讲解 Server Box 中 BMC 功能的配置模型、TLS 首次信任机制、厂商差异处理、轮询策略、Phase 1 功能范围与真实硬件验证方法,帮助读者理解该功能的实现边界与设计取舍。
为什么需要 BMC:主机不可用时的独立信息源
SSH 和 Monitor agent 都要求主机操作系统正常运行:SSH 需要sshd,Monitor agent 需要在主机上运行进程。当主机断电、卡死或重启时,二者通常只能报告连接失败。
BMC(Baseboard Management Controller)是主板上的独立计算机,拥有独立的供电和网络接口。即使主机上的其他服务全部不可用,BMC 仍可能响应,提供电源状态、硬件传感器读数和硬件事件等主机操作系统无法提供的信息。Server Box 正是利用这一特性,把 BMC 作为 SSH / Monitor 之外的"侧通道"能力。
⚠️ 当前 BMC 功能处于Beta阶段:只有读取功能(电源状态和传感器)在一台真实设备上验证过,电源控制尚未进行自动化验证。文档中的硬件差异主要来自厂商文档、录制的响应和本地测试服务,不能视为硬件兼容列表。请把电源操作当作按下远程服务器上的物理电源按钮。
为什么选择 Redfish 而不是 IPMI
Server Box 通过 Redfish 的 HTTPS/JSON API 与 BMC 通信。Redfish 是现代服务器常见的带外管理接口,与 IPMI 2.0 over LAN 的对比决定了这一选择:
| 对比项 | Redfish | IPMI 2.0 over LAN |
|---|---|---|
| 传输 | HTTPS + JSON | RMCP+ over UDP 623,二进制协议 |
| 在本项目中的实现成本 | 用dio即可实现,不需要原生代码 | 没有 Dart 实现,需要通过 FFI 在crates/中实现客户端 |
| 数据模型 | 自描述资源,可通过链接遍历 | SDR、SEL、chassis 命令及厂商自定义 raw 数据 |
| 认证 | TLS + session token | RAKP |
| 硬件覆盖 | 通常为 2016 年及之后的设备 | 还覆盖更早和入门级设备 |
| 规范状态 | 持续更新 | 最近一次修订在 2013 年 |
IPMI 的优势主要是支持更早的硬件和 Serial-over-LAN。当前项目暂不引入 IPMI 客户端;如果未来需要覆盖 Redfish 之前的设备,需单独评估 FFI 客户端和另一套安全模型。
在应用模型中的位置:挂在 Spi 上的侧通道
BMC 是主机的带外管理通道,属于服务器配置中的独立侧通道。服务器的 SSH 和 Monitor HTTP 配置决定状态数据和常规操作从哪里获取;BMC 用于主机操作系统不可用时读取硬件状态和执行电源操作。
BMC 配置挂在Spi上,与 Wake-on-LAN 的wolCfg同级:
class Spi { SshCredential? ssh; MonitorHttpCredential? monitorHttp; WakeOnLanCfg? wolCfg; BmcCfg? bmc; // 未配置 BMC 时为 null } final class BmcCfg { String addr; // https://... String? credId; // 多台服务器共用 BmcCredential String? certSha256; // 用户确认后固定 } class BmcCredential { String id; // 生成的 ID,不是显示名称 String name; // 选择器中显示的唯一名称 String user; String? pwd; }这一模型在源码中有更细的约束。BmcCfg(lib/data/model/server/bmc_cfg.dart)只有三个字段,其中addr只取 scheme、host 和 port,/redfish/v1/服务根路径是固定的,由客户端追加而非配置;uri与port按 scheme 缺省端口(HTTPS→443、HTTP→80),且只接受https/http,因此10.0.0.9、ftp://...这类非法地址在编辑器阶段就会被拒绝;isComplete只要求"地址 + 账户",证书不参与判断——因为核对证书需要先能连上设备,两者有先后依赖。BmcCredential(lib/data/model/server/bmc_credential.dart)中id是生成的稳定标识,永不使用name作为引用——这是从私钥"name 即 id"导致改名后所有引用服务器全部脱钩的教训中抽象出的同一决策;pwd可为 null,用于表达"已命名但尚未录入密码"的状态,而不是存储一个空字符串冒充密码。
账户与设备分离的设计理由
- BMC 账户是独立记录,按 ID 引用。机架中的多台服务器通常共用一组 BMC 账户,因此修改一次账户即可更新所有引用它的服务器。如果把密码复制到每台服务器,轮换密码时就要改二十个地方,漏改哪一台只能靠"机器不再应答"来发现。
- 删除账户使用
ON DELETE SET NULL,不级联删除服务器。见 lib/data/provider/bmc_credential.dart 的delete():它会先找出所有spi.bmc?.credId == cred.id的服务器并逐个把credId置空,再删除账户记录。之所以要在应用层显式清理而非只依赖外键,是因为:直接写列不会移动updated_at和rev,同步对端才不会把悬空的 id 又同步回来;同时ServerStore从缓存应答,删除操作对它不可见,若不清理,受影响服务器下次保存时会把已删除的 id 写回,在编辑器里爆出外键错误。 - 地址和证书 fingerprint 属于单台 BMC,保存在
BmcCfg中。不同 BMC 使用不同证书,且永远不会有两台 BMC 出示同一张证书;如果把 fingerprint 放在共享账户记录里,第一台设备的 fingerprint 会被拿去验证第二台设备——等于完全没有校验。 - 物理主机和虚拟机可能共同指向一台 BMC。此时从任意虚拟机执行电源操作都会影响整台物理主机,读到的
PowerState也是主机状态。当前模型不处理这种 host/guest 关系,文档建议只在物理主机记录上配置 BMC。
分层设计:让 fixture 可验证的代码不依赖真实服务器
BmcCfg + BmcCredential 用户配置 App ↓ RedfishClient TLS 信任、session、GET/POST package:redfish RedfishDiscovery 一次性发现资源和 reset 类型 package:redfish resources / sensors JSON → model,不执行 IO package:redfish ↓ BmcNotifier 状态和独立轮询周期 App只有RedfishClient访问网络;下层解析组件接收已获取的 JSON map 并返回 model。因此厂商差异可以通过保存的响应(fixture)测试,而不必每次连接真实硬件。协议侧被抽到了独立的 packages/redfish 包,由 pubspec.yaml 以path:方式引用,它不关心 Server Box 如何存储配置。
TLS 与首次使用信任:BMC 证书如何被安全确认
BMC 通常使用自签名证书。BMC 具有电源控制权限,接受地址上的任意证书会允许其他服务冒充 BMC,因此 Server Box 采用与 SSH host key 类似的**首次使用信任(TOFU)**机制:
- 首次连接时显示证书的 SHA-256 fingerprint。
- 你在 BMC 自带的 Web 界面中核对 fingerprint,并确认是否信任。
- App 保存已确认的 fingerprint,后续只接受匹配的证书。
- fingerprint 变化时拒绝连接,显示新旧值并要求重新确认。
实现上,BmcCfg.certSha256保存的是证书 DER 形式的 SHA-256(小写十六进制);字段缺失意味着"尚未核对过",此时连接会被拒绝而非信任——这是有意为之:PVE 和 monitor agent 都带"忽略证书"开关,而一个握着电源控制权的管理接口,是最不该沿用这种开关的地方。copyWith(certSha256: null)被显式支持,以便用户清除已固定的证书(见 test/bmc_cfg_test.dart 中的copyWith can clear the pinned certificate用例)。
BMC 重新生成证书或升级固件后,fingerprint 可能正常变化;中间人攻击也会造成相同现象。接受新证书前,请先确认 BMC 的身份。
厂商差异:Redfish 资源模型里的坑
以下内容都需要通过 Redfish 资源发现,不能根据厂商名称或常见路径硬编码。不同固件可能实现不同版本的资源模型。
资源 ID 不固定
/redfish/v1/是固定入口,但Systems和Chassis下的 ID 由厂商决定:
| 厂商 | 典型的 system ID |
|---|---|
| Supermicro | 1 |
| Dell iDRAC | System.Embedded.1 |
| OpenBMC | system |
客户端应遍历/redfish/v1/Systems,读取集合返回的Members,然后访问具体资源。这也解释了为何BmcCfg只保存服务根地址:Systems/1、Chassis/1这类路径必须通过发现得到。
传感器模型可能有两套
Redfish 2020.4 将Chassis/{id}/Thermal和/Power标记为旧模型,推荐使用ThermalSubsystem、PowerSubsystem和统一的Sensors集合。许多固件仍然只实现旧模型,也有固件同时提供两套。客户端应检查Chassis资源实际提供的 links:优先使用完整的新模型,缺少新模型时再使用旧模型,不要根据厂商名称推断模型。已验证的 H3C 设备就是这种"两套并存但不完整"的典型:Sensors有链接但ThermalSubsystem不完整,因此回退到旧模型(Thermal+Power)。
ResetType的声明可能不可靠
ComputerSystem.Resetaction 会通过ResetType@Redfish.AllowableValues声明支持的值。不同设备支持的值不同,Nmi和PowerCycle尤其可能因固件实现或 license 限制而不可用。App 将"重启""电源循环"等用户意图映射到设备声明的值,并为必要操作提供降级顺序;找不到可用值时,不显示对应按钮。
厂商可能扩展ResetType
H3C R5350 G6 会声明标准枚举中不存在的ForcePowerCycle,同时不声明其他断电操作。如果只匹配标准名称,就会错误地把"电源循环"映射为ForceRestart(重启而非断电再上电)。因此每个用户意图的候选列表会在标准名称之后包含已知的厂商扩展名称;扩展名称来自实际设备响应,不能只根据规范推断。
传感器可能返回哨兵值
规范建议没有读数时返回null,但有些固件会返回哨兵值。例如测试过的 H3C 设备将无法读取的温度返回4294967295(0xFFFFFFFF)。App 按合理范围过滤读数,而不是只匹配已知哨兵值:当前过滤范围不会接受低于绝对零度或高于 1000°C 的温度,也不会接受不合理的风扇转速。-1被保留,因为它既可能是哨兵值,也可能是真实的低温读数。
Graceful 操作只表示请求已接受
HPE iLO 文档说明GracefulShutdown和GracefulRestart的行为取决于操作系统,204 No Content只能说明请求已被接受,不能证明操作系统已经执行。因此 App 会轮询PowerState,直到观察到状态变化或达到超时,HTTP status code 不作为电源操作结果。
Session 数量有限
Redfish session 通过POST到SessionService/Sessions创建,并在响应中返回X-Auth-Token。未删除的 session 会在 BMC 上保留到超时;BMC 的并发 session 数量通常很少,泄漏过多可能导致管理界面拒绝新连接。每个 client 只创建一个 session,并在销毁时DELETE对应资源;正常路径和失败路径都必须释放 session;只探测服务根资源时无需创建 session。
License 可能限制功能
部分 Supermicro license 会限制固件更新和虚拟介质等功能。测试表明,某些 X11 到 X13 设备的只读 GET 和ComputerSystem.Reset不需要这些 license——这属于设备经验,不代表所有型号的固定政策。子资源返回401或403时,App 应如实报告该资源不可用,并继续处理其他资源。
轮询策略:独立周期 + 一次性发现 + 并发保护
BMC 的响应通常比操作系统 API 慢,一次传感器读取可能需要几秒。因此 BMC 使用独立的轮询周期,不与常规服务器状态轮询共用计时器。在 lib/data/provider/bmc/bmc.dart 中可以确认这些关键常量:
_pollInterval = Duration(minutes: 1):BMC 轮询周期为一分钟。原因在源码注释中写得很清楚:BMC 很慢、回答的是硬件数据,变化速率远低于状态页;搭常规轮询的便车会把设备大部分容量花在没人看的变化上。_powerConfirmTimeout = Duration(minutes: 2)与_powerPollInterval = Duration(seconds: 5):电源操作后每 5 秒轮询一次PowerState,最多等 2 分钟。因为 HTTP 状态不是答案,答案在PowerState里。_maxSensorMembers = 64:新模型把每个读数放在独立资源里,一百个传感器就是一百次请求;不设上限一次轮询会拖到下一次开始。截断时通过BmcState.sensorsTruncated明确告知,避免"静默截断读起来像这台机器只有 64 个传感器"。
资源 ID、传感器模型和可用 reset 类型属于每个 client 的 discovery 结果,只发现一次并缓存;每次轮询只读取和转换实时数据,避免重复请求设备结构。BmcNotifier持有一个RedfishClient(即设备上一个 session),因此dispose时关闭 client 是唯一把 session 还回去的途径(设备允许的 session 极少)。
并发保护上,_refreshing标志保证慢轮询与电源确认操作不重叠——电源确认最长 2 分钟、而定时器 1 分钟触发一次,不加保护就会同时出现两个 discovery 和最多 64×2 次传感器请求;策略是"跳过而不是排队",因为轮询产出的是当前状态,已经在跑的那次马上就会发布它。此外用_generation计数器让配置变更后仍在途的请求能识别自己的答案已过期。
电源操作的结果枚举BmcPowerResult也体现了"状态变化才算结果"的设计:confirmed表示PowerState真的移动了(唯一"机器做了点什么"的证据);accepted表示服务接受了请求但等待超时前状态未变——这不是失败(graceful shutdown 可能比任何人愿意等的都久),但也不能当结果上报;notSupported表示设备声明中没有任何值能满足该意图,请求根本没发出;failed表示失败。
Phase 1 功能范围
- 探测
GET /redfish/v1/及其集合 - 读取
Systems/{id}:电源状态、机型、序列号、BIOS 版本和健康汇总 - 读取
Chassis/{id}:温度、风扇转速和功耗,使用设备提供的传感器模型 - 调用
ComputerSystem.Reset:显示确认对话框,协商 reset 类型,并通过轮询确认结果
暂不支持:事件日志、存储清单、启动项覆盖、虚拟介质,以及通过 Monitor agent 中继访问隔离网络中的 BMC。
已验证硬件:H3C R5350 G6
下表记录的是已经实际响应过的设备,不是兼容性列表;其他行为来自厂商文档、录制的响应和本地测试服务。
| 项目 | 值 |
|---|---|
| 型号 | H3C R5350 G6 |
| BIOS | 6.30.50 |
| Redfish 版本 | 1.15.1 |
根节点Vendor/Product | 两者都缺失,不能依赖它们识别服务 |
| System ID | Systems/1 |
| Chassis ID | Chassis/1 |
| 传感器模型 | legacy(Thermal+Power);Sensors有链接但ThermalSubsystem不完整,因此使用旧模型 |
ResetType | ForceOff、ForcePowerCycle、ForceRestart、GracefulShutdown、Nmi、On |
| 温度 | 20 个,其中 18 个返回0xFFFFFFFF哨兵值 |
| 风扇 | 8 个位置,每个位置返回两条不同读数 |
| 整机功耗 | PowerControl返回 48 W |
| Session | 一个 client 保持一个 session,关闭时释放 |
| 证书 | 自签名,测试时在有效期内 |
| 测试时间 | 2026-08-23 |
尚未覆盖:Dell(System.Embedded.1)、OpenBMC(system)、Supermicro X11–X14 的传感器模型差异、HPE iLO 的 graceful 操作,以及提供多个 system 的设备。
如何验证真实硬件
只读测试
packages/redfish/test/e2e_test.dart是 opt-in 的只读测试。在工作区根目录的.env中设置以下变量后才会连接真实设备;未设置时会静默跳过:
SBM_E2E_BMC_URL=https://10.0.0.9 SBM_E2E_BMC_USER=... SBM_E2E_BMC_PWD=...测试会打印发现到的资源 ID、传感器模型、reset 类型和每个用户意图的映射结果,不会断言某个厂商必须使用某种形状。它会读取 reset action,但不会向 action 发送 POST。
电源操作(手工验证)
电源控制没有自动化测试。手工验证时,请使用不承载重要业务的设备:
- 配置 BMC,并核对证书 fingerprint。
- 确认 App 显示的电源状态与设备实际状态一致。
- 点击重启,在确认对话框中核对协商得到的
ResetType。 - 检查结果是confirmed还是accepted。对于 iLO 的 graceful 操作,accepted是合理结果;对于明确区分请求和结果的设备,accepted表示尚未观察到状态变化,需要进一步检查。
总结
Server Box 的 BMC 功能围绕三条主线设计:配置模型上把账户(机架共享)与地址/证书(单设备独有)彻底分离,避免共享凭证污染设备校验;协议实现上坚持"一切通过资源发现,不按厂商名称硬编码",并逐一处理了资源 ID、传感器双模型、厂商扩展ResetType、哨兵值、graceful 语义、session 配额和 license 限制等真实世界差异;运行时策略上用独立轮询周期、一次性 discovery 缓存和严格的并发保护,适配 BMC"慢、连接数少、但提供主机不可替代信息"的硬件特性。当前功能处于 Beta,电源控制仅经一台设备手工验证,随着更多真实硬件(Dell、OpenBMC、Supermicro、HPE iLO)加入验证,厂商差异清单还会继续扩展。
【免费下载链接】flutter_server_boxServerBox - server status & toolbox项目地址: https://gitcode.com/GitHub_Trending/fl/flutter_server_box
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考