log-redfish-lts · 让任意 Redfish 路径只靠配置即可采集数据
不同厂商、不同型号的 BMC(基板管理控制器)暴露传感器数据的 接口路径和 JSON 结构千差万别:
/redfish/v1/Chassis/1/ThresholdSensors/sensors/allinfo、/kunlun/webui/sensorSensors[*].Name),有的是字典(data.{*}.Value)如果把"取哪个路径、取哪个字段"写死在代码里,每换一台机器就要改代码、重新发版。指纹机制的做法是:
指纹文件默认为 uri_fingerprints.json(可在 config.ini 里改 fingerprint_file),是一个 JSON 数组,每个元素是一条指纹:
[
{
"id": 1, // 整数,全局唯一,用于 fingerprint_id 指定或日志引用
"type": "redfish_temp_power_speed_duty", // 字符串,人类可读的类型名,仅用于日志展示
"Uris": { // 对象:键=URI路径,值=该路径的字段配置
"/redfish/v1/Chassis/1/ThresholdSensors": {
"fields": [ /* 见第三章 */ ]
},
"/redfish/v1/Chassis/1/Oem/Public/Thermal": {
"fields": [ /* 一条指纹可同时从多个 URI 聚合数据 */ ]
}
}
},
{ "id": 2, "type": "...", "Uris": {/* ... */} }
]
每个 URI 下 "fields" 是数组,数组里每个对象描述"一个要提取的量"。根据 path 里通配符的不同,分三种模式:
[*] —— 传感器是一个数组当接口返回形如 "Sensors": [ {...}, {...} ] 的数组时使用。程序会遍历数组每个元素,从中各取一个 name 和若干 value。
| fields 配置 | 典型响应 | 提取结果 |
|---|---|---|
[
{"name":"name", "path":"Sensors[*].Name"},
{"name":"value", "path":"Sensors[*].ReadingValue"}
]
|
{
"Sensors": [
{"Name":"CPU0_Temp","ReadingValue":45},
{"Name":"CPU1_Temp","ReadingValue":47}
]
}
|
{
"CPU0_Temp": 45,
"CPU1_Temp": 47
}
|
规则:同一 [*] 前缀的字段会按 name 配对。name 字段的值作为结果键名;其余字段作为值。若只有一个 value 字段且名为 value,结果键就是 name 本身;否则键名为 传感器名_字段名(见下文命名规则)。
[*] 之前可以是多层路径,例如
Oem.Public.Fans[*].FanName。旧版引擎只支持顶层键(如 Sensors[*].Name)。
{*} —— 传感器是一个字典当接口返回形如 "data": { "CPU0": {...}, "CPU1": {...} } 的对象时使用。程序遍历字典每个键值对。
| fields 配置 | 典型响应 | 提取结果 |
|---|---|---|
[
{"name":"name", "path":"data.{*}.@key"},
{"name":"value", "path":"data.{*}.Value"}
]
|
{
"data": {
"CPU0": {"Value":45},
"CPU1": {"Value":47}
}
}
|
{
"CPU0": 45,
"CPU1": 47
}
|
关键点:@key 是一个特殊后缀,表示"用字典的键本身作为传感器名"。如果不写 @key,则需要再配一个 name 字段指向某个属性来取名字。
data.{*}.Sensor.Value(取 data 下每个对象的 Sensor.Value)。旧版只支持一层。
当只需要从响应里取某个固定位置的值(比如设备型号、序列号、单一读数)时使用。path 直接是嵌套路径,不含 [*] 或 {*}。
| fields 配置 | 典型响应 | 提取结果 |
|---|---|---|
[
{"name":"model", "path":"Oem.DeviceInfo.Model"},
{"name":"sn", "path":"SerialNumber"}
]
|
{
"SerialNumber":"ABC123",
"Oem":{"DeviceInfo":{"Model":"X2000"}}
}
|
{
"model": "X2000",
"sn": "ABC123"
}
|
fields 数组里可以同时出现三种模式的字段,程序会分别处理再合并到同一行结果。
| 写法 | 含义 | 示例 |
|---|---|---|
Key | 取对象下某键 | SerialNumber |
a.b.c | 多层嵌套(点分隔) | Oem.DeviceInfo.Model |
[*] | 遍历数组每个元素 | Sensors[*].Name |
{*} | 遍历字典每个键值对 | data.{*}.Value |
@key(仅字典模式 name) | 用字典键本身作传感器名 | data.{*}.@key |
数字(在路径中) | 数组索引(0 起) | Fans.0.Name 取第 1 个 |
Name ≠ name。
建议先用 curl -k -H "X-Auth-Token: xxx" https://<ip>/ 路径 看真实响应再写 path。
提取出来的数据最终拼成一行 CSV,列名(键名)的生成规则:
| 情况 | 键名 | 示例 |
|---|---|---|
只有一个 value 字段,且其 name 为 "value" |
直接用传感器名 | CPU0_Temp |
有多个值字段(如 rpm + duty),或 value 的 name 不叫 value |
传感器名_字段name |
Fan0_rpm、Fan0_duty |
字典模式用 @key |
字典键(若含 / 取最后一段) |
键 root/sensor/CPU0 → CPU0 |
| 固定字段模式 | field 的 name |
model |
name_rpm / name_duty 区分。
程序启动后调用 probe() 匹配指纹,有两种触发方式:
config.ini 里 不写 fingerprint_id(或注释掉)。程序会:
config.ini 里写 fingerprint_id = 2。程序直接用 id=2 的指纹,但仍会验证它的所有 URI:
DataCollectionError 并退出(避免采到空数据)。提取出来的原始结果,还会过一道清洗,以下值会被整列丢弃(不出现在 CSV 里):
| 无效值 | 含义 |
|---|---|
65535 / 65535.0 | 传感器故障或未接入时的占位标记 |
null / None | 字段缺失 |
空字符串 "" | 空读数 |
"NA"(不区分大小写) | Not Available |
name 字段或 @key),那个传感器会被跳过。
固定字段模式则用 field 自己的 name 当列名。
Name 写成 name 导致提取为空。
Sensors[*].Name 要求 Sensors 的值是 [...]。
如果实际是 {"0":{...}} 这种"伪数组",改用字典模式 Sensors.{*}.Name。
@odata.id 指向另一个 URL,它本身不是读数。要取的数据应在叶子节点。
/redfish/v1/...,程序内部会拼到 host 上。不要写完整域名。
logs/ 下会记录每个 URI 的采集诊断(成功提取几个字段、响应空、字段不匹配等)。
配不通时先看日志,再 curl 对比真实结构。
[
{
"id": 1,
"type": "redfish_standard",
"Uris": {
"/redfish/v1/Chassis/1/ThresholdSensors": {
"fields": [
{"name":"name", "path":"Sensors[*].Name"},
{"name":"value", "path":"Sensors[*].ReadingValue"}
]
},
"/redfish/v1/Chassis/1/Oem/Public/Thermal": {
"fields": [
{"name":"name", "path":"Fans[*].Name"},
{"name":"duty","path":"Fans[*].SpeedRatio"}
]
}
}
},
{
"id": 2,
"type": "oembmc_dict_style",
"Uris": {
"/sensors/allinfo": {
"fields": [
{"name":"name", "path":"{*}.11"},
{"name":"value", "path":"{*}.1"}
]
}
}
},
{
"id": 3,
"type": "kunlun_webui",
"Uris": {
"/kunlun/webui/sensor": {
"fields": [
{"name":"name", "path":"data.{*}.@key"},
{"name":"value", "path":"data.{*}.Value"}
]
}
}
},
{
"id": 4,
"type": "new_machine_with_nested_paths",
"Uris": {
"/redfish/v1/Chassis/1/Thermal": {
"fields": [
{"name":"name", "path":"Oem.Public.Fans[*].FanName"},
{"name":"rpm", "path":"Oem.Public.Fans[*].ReadingRPM"}
]
},
"/redfish/v1/Chassis/1": {
"fields": [
{"name":"model", "path":"Oem.DeviceInfo.Model"},
{"name":"sn", "path":"SerialNumber"}
]
}
}
}
]
Oem.Public.Fans[*].FanName、
字典多层后缀、固定字段 Oem.DeviceInfo.Model 三种 v2 能力的混合用法。
| 现象 | 可能原因 | 排查 |
|---|---|---|
| 未匹配到任何指纹 | 所有指纹的 URI 在该机器都不可用 / fields 都提取为空 | 看日志诊断;curl 各 URI;核对 path 大小写 |
| 匹配成功但 CSV 某些列空 | 那次该传感器值=65535/NA 被过滤,或该 URI 偶发失败 | 正常;持续空则检查该 URI 稳定性 |
| 提取结果为空(字段不匹配) | path 写错 / 键名大小写不符 / 数据结构变了 | curl 看真实响应,逐段对照 path |
| 数组模式提取为空 | 根不是数组(是字典),应改用 {*} | 检查响应里该键的值是 [...] 还是 {...} |
| 嵌套路径提取不到(v2 特性) | 代码还是旧版引擎 | 确认 jsonpath.py 已升级(含 fixed_fields、_get_nested_value 导航) |
| 固定字段模式不生效 | 同上,旧版引擎忽略无通配符 path | 升级到 v2 引擎 |
| 指定 fingerprint_id 报错退出 | 该指纹所有 URI 都提取不到数据 | 看报错诊断信息;机器可能不支持该指纹 |
| 会话过期反复重登 | token 超时 | 程序已自动重登,无需处理;若频繁可缩短 interval |