Provide openUBMC SNMP interfaces enabling system information query and configuration operations
当前访问频次受限,请登录后继续访问
snmp
1. 组件概述
1.1 组件简介
snmp(简单网络管理协议)提供了 openUBMC 对外的访问接口之一,以 SNMP 协议实现。SNMP 接口包括 SNMP 查询和 SNMP设置。SNMP查询接口用于查询openUBMC系统的信息,SNMP设置接口用于设置、操作openUBMC系统。
1.2 解决了什么问题
简单网络管理协议 SNMP(Simple Network Management Protocol)用于网络设备的管理。网络设备多种多样,不同设备尝厂商提供的设备管理接口(如命令行接口)各不相同,这使得网络管理变得愈发复杂。为了解决这一问题,SNMP 应运而生。SNMP 作为广泛应用与TCP/IP网络的网络标准管理协议,提供了统一的接口,从而实现了不同种类和厂商的网络设备。openUBMC 的 snmp 组件由 net-snmp 的 snmpd 进程加载运行。组件本身不提供 D-Bus 服务、不直接处理 IPMI 命令,而是作为 SNMP 协议与资源协作接口之间的「协议代理 / 适配层」:将标准 SNMP Get/Set 请求映射到资源协作接口对象读写,新增 SNMP 可管理对象时无需修改 C 代码,只需新增一份映射配置。
1.3 核心功能:
- 接口注册:依据映射配置把每条 SNMP 接口注册为 net-snmp 的 MIB 对象(标量或表),支持
Readwrite/Readonly/Setonly三种访问模式。 - Get/Set 处理:接收 net-snmp 请求,经路由映射(
route_mapper)转换为资源协作接口属性读写。 - 类型转换:在 ASN.1 类型(
INTEGER、OCTET STRING、IpAddress、OBJECT IDENTIFIER)与 JSON 之间双向转换。 - 表接口缓存:对 table 类型 GET 做 1s 短缓存,降低资源协作接口重复遍历开销。
- 异步任务关联:支持
TaskReferenceUri,将 SNMP 请求关联到资源协作接口异步任务对象。 - 访问来源记录:记录到达 BMC 的 SNMP 报文(来源 IP/端口、OID),用于审计与定界。
- 可定制 OID 前缀:支持通过
SnmpOemIdentifier替换 OEM OID 前缀。
1.4 与外部系统的交互边界:
SNMP 系统由网络管理系统 NMS(Network Management System)、SNMP Agent 、被管理对象 Management object 和管理信息库 MIB(Management Information Base)四部分组成。NMS 作为整个网络的网管中心,对设备进行管理。每隔被管理的设备中都包含驻留在设备上的 SNMP Agent 进程、MIB 和多个被管对象。NMS 提供与运行在被管理设备上的 SNMP Agent 交互,由 SNMP Agent 通过对设备端的MIB进行操作,完成 NMS 的指令。 SNMP 系统组成如下图所示:
flowchart LR
subgraph Probe[Device]
B(SNMP Agent) <--> C(MIB)
C(MIB) <--> D(Managed object)
end
A(NMS) <--> B(SNMP Agent)
2. API 使用说明和示例
组件对外 API 分两层:Lua 全局 API(src/lualib/main.lua 导出,由 C 层调用)与 C 层入口函数(编译进 libsnmp.so)。
2.1 match
功能说明
接收一条 SNMP 请求,经路由映射转换为资源协作接口访问并返回结果,是 C 层 handler 与 Lua 层的桥接点。
参数说明
| 参数 | 类型 | 描述 | 必选 |
|---|---|---|---|
| uri | string | 接口 URI,形如 /snmp/<oid>/<接口名>/<模式> |
是 |
| method | string | GET / PATCH / POST |
是 |
| address | string | 客户端 IP | 是 |
| username | string | SNMP 用户名(v1/v2c 为 SNMPv1_v2c) |
是 |
| reqbody | string | 请求体 JSON,GET 可为 nil | 否 |
返回值与异常
固定 3 个值 err_code, ret_type, rsp_body。err_code 为 0(SNMP_ERR_NOERROR)/5(SNMP_ERR_GENERR)或错误消息中的 SnmpStatusCode;ret_type 为 "number"|"string"|"table"|"userdata";rsp_body 为响应值或错误描述字符串。
异常与触发条件:函数内部用 pcall 包裹,不向调用方抛异常,以 err_code + 描述返回。非 0 的典型触发:映射配置缺失、映射匹配抛异常、映射返回非空错误列表、响应体出现 null、响应体 JSON 编码失败。
2.2 其余 Lua API
| API | 作用 | 返回 | 异常 |
|---|---|---|---|
get_all_interface_uri_config() |
返回所有 SNMP 接口的 URI/Name/Oid/Mode(启动注册用) | JSON 字符串数组 | 不满足校验的 URI 被跳过并打印错误日志 |
get_sequence_elements_count(uri) |
返回接口 Sequence 元素个数,区分标量/表 |
整数(标量 0) | 无(非法返回 0) |
get_interface_reqbody_config(uri) |
返回 Set 请求体配置 | JSON {Type, ReqBody:[{Name,Type}]} |
空配置返回 "" |
get_interface_primary_key(uri) |
返回表接口主键名称与类型 | JSON 数组 [{Name,Type}] |
无(无 Sequence 返回 []) |
get_interface_instance(uri) |
返回表接口实例信息(带 1s 缓存) | JSON {Count, PK, Index} |
失败返回 "" |
get_interface_sequence(uri) |
返回表接口 Sequence 列描述 |
JSON 数组 | 无 Sequence 返回 "" |
类型约束:ReqBody 属性类型仅支持 integer/string;Sequence 列 Type 支持 integer/string/IpAddress/ObjectIdentifier;主键类型仅 integer/string。
C 层入口函数:init_snmp_proxy()(初始化:加载 Lua 库/主脚本、注册接口)、register_interface()(注册标量/表接口)、interface_handle(...)(net-snmp handler 回调,分发 Get/Set 并记录访问来源)。
2.3 调试示例
本组件是 library,运行载体为 snmpd,调试入口为 net-snmp 客户端工具(snmpget/snmpwalk/snmpset/snmptable),非 busctl/mdbctl。
# 遍历本组件注册的 SNMP 接口(OEM OID 子树)
snmpwalk -v2c -c <community> <bmc_ip> 1.3.6.1.4.1.2011.2.235.1.1
# 读取标量接口(OID 后追加 .0;示例 trapEnable,见 test/unit/data/mapping_config/config.lua)
snmpget -v2c -c <community> <bmc_ip> 1.3.6.1.4.1.2011.2.235.1.1.4.1.0
# 示意响应(需环境验证):iso.3.6.1.4.1.2011.2.235.1.1.4.1.0 = INTEGER: 1
# 设置标量接口
snmpset -v2c -c <community> <bmc_ip> 1.3.6.1.4.1.2011.2.235.1.1.4.1.0 i 2
# 遍历表接口(示例 trapInfoDescriptionTable)
snmptable -v2c -c <community> <bmc_ip> 1.3.6.1.4.1.2011.2.235.1.1.4.50
SNMPv3 需追加 -v3 -l authPriv -u <user> -a <authproto> -A <authpass> -x <privproto> -X <privpass>。
3. 组件扩展案例
3.1 扩展能力概述
核心扩展能力是基于映射配置新增/定制 SNMP 接口,无需改代码。映射配置为 JSON 格式,源文件随产品仓提供,参考 rackmount 仓 rackmount/interface_config/snmp/(config.json 为全局变量配置,mapping_config/ 为接口映射配置,plugins/、script/、mib/ 分别为编排插件、校验脚本与 MIB 文件)。
3.2 扩展点:
(1)接口映射配置目录为 /opt/bmc/apps/snmp/interface_config/<产品目录>/mapping_config/(未区分产品时即 /opt/bmc/apps/snmp/interface_config/mapping_config/),其下按功能模块存放 .json 映射文件。
(2)加载映射配置时,组件从 innerbus 读取 platform_id、board_id 两个寄存器值,拼接为目录名 <platform_id>_<board_id>(十六进制 %02x_%02x);若目录 /opt/bmc/apps/snmp/interface_config/<platform_id>_<board_id> 存在,则使用该路径,否则使用默认路径 /opt/bmc/apps/snmp/interface_config。
3.3 二次开发指导:
config.json 定义 OEM OID 变量,供映射配置中的 {{SnmpOemIdentifier}} 占位符引用:
{
"GlobalVariable": {
"SnmpOemIdentifier": "2011.2.235.1.1"
}
}
各模块 .json 含 Resources 数组,每条资源描述一个 SNMP 接口,Uri 中经 {{SnmpOemIdentifier}} 引用上述变量;Interfaces 描述该接口的 Get/PATCH/POST 行为(RspBody/ReqBody/Statements/ProcessingFlow):
示例
(标量接口 trapEnable,节选自 rackmount/interface_config/snmp/mapping_config/trap/trap.json):
{
"Resources": [
{
"Uri": "/snmp/1.3.6.1.4.1.{{SnmpOemIdentifier}}.4.1/trapEnable/Readwrite",
"Interfaces": [
{
"Type": "Get",
"RspBody": { "Enabled": "${Statements/Enabled()}" },
"Statements": {
"Enabled": {
"Input": "${ProcessingFlow[1]/Destination/Enabled}",
"Steps": [ { "Type": "Switch", "Formula": [ { "Case": false, "To": 1 }, { "Case": true, "To": 2 } ] } ]
}
},
"ProcessingFlow": [
{ "Type": "Property", "Path": "/bmc/kepler/EventService/Subscriptions/Snmp",
"Interface": "bmc.kepler.EventService.Subscriptions.Snmp",
"Destination": { "Enabled": "Enabled" } }
]
},
{
"Type": "PATCH",
"ReqBody": { "Type": "object", "Required": true, "Properties": { "Enabled": { "Required": true, "Type": "integer", "Validator": [ { "Type": "Enum", "Formula": [ 1, 2 ] } ] } } },
"Statements": { "Enabled": { "Input": "${ReqBody/Enabled}", "Steps": [ { "Type": "Switch", "Formula": [ { "Case": 1, "To": false }, { "Case": 2, "To": true } ] } ] } },
"ProcessingFlow": [
{ "Type": "Property", "Path": "/bmc/kepler/EventService/Subscriptions/Snmp",
"Interface": "bmc.kepler.EventService.Subscriptions.Snmp",
"Source": { "Enabled": "${Statements/Enabled()}" } }
]
}
]
}
]
}
URI 格式与约束:/snmp/<oid>/<接口名>/<模式>。
<oid>前缀须为通用前缀1.3.6.1.2.1.1.或 OEM 前缀1.3.6.1.4.1.{{SnmpOemIdentifier}}.(默认SnmpOemIdentifier = 2011.2.235.1.1,即1.3.6.1.4.1.2011.2.235.1.1.);- URI 按
/拆分须为 5 段;接口名非空; <模式>为Readwrite/Readonly/Setonly;- 表接口需配置
Sequence数组,元素含Name/Type/Access(Readonly/Readwrite/Setonly)/Primary(主键标记),完整示例见rackmount/interface_config/snmp/mapping_config/trap/trap.json中的trapInfoDescriptionTable。
验证方法:
将 JSON 配置放入映射目录并重启 snmpd → 用 snmpwalk/snmpget/snmpset 验证(见 2.3);单元测试 test/unit(test_interface.lua)使用 Lua 形式配置样例校验 URI 解析、请求体类型、主键数量、Sequence 类型等约束。
注意事项:
- 表接口必须配主键(
Primary=true,数量不为 0);简单接口ReqBody属性个数应为 1。 - URI 不满足校验规则会被跳过(打印错误日志)。
- 列
Access为Readonly时,Set 会被拒(SNMP_ERR_NOACCESS)。 - 产品专属配置需保证读取到 platform/board 信息且对应目录存在。
4. 日志说明
组件统一使用日志模块名 snmp(Lua 层 log:set_log_module_name("snmp"),C 层 set_log_module_name("snmp")),经统一日志框架输出。
关键日志信息:
| 日志 | 含义 |
|---|---|
initialize the Proxy of SNMP interface successfully.(INFO) |
组件初始化成功 |
load the lua lib failed. / load the main start script failed.(ERROR) |
Lua 库/主脚本加载失败 |
register the SNMP interface failed.(ERROR) |
接口注册失败 |
create the mapper of SNMP interface failed, error is %s.(ERROR) |
创建路由映射器失败 |
The route configuration is missing.(ERROR) |
路由配置缺失 |
the prefix of Uri(%s) should be ... / the Uri(%s) is invalid... / the prefix of Oid... / the interface name... / the mode of Uri...(ERROR) |
URI/OID/接口名/模式校验失败,接口被跳过 |
get the instance of interface(%s) failed, error is %s(ERROR) |
表接口实例获取失败 |
the response body is nil / encode the response body failed, error is %s(ERROR) |
响应体为空/编码失败 |
the response body of task is null.(NOTICE) |
任务接口响应体为空 |
访问来源记录(net stream):每条到达 BMC 的 SNMP 报文经 syslog(LOG_LOCAL7 | LOG_ERR, ...) 记录一条来源日志(handler.c):
Received SNMP packet(s) from UDP: [<客户端IP>]:<端口>. oid:<OID>
未解析字段以 N/A 填充,是「一键日志收集」中定位 SNMP 访问来源的关键信息。
5. 问题定界指南
5.1 典型问题与定界:
| 现象 | 归属判断 | 关键日志/依据 |
|---|---|---|
snmpwalk/snmpget 返回 genErr(5) |
snmp 组件或资源协作接口 | The route configuration is missing、the response body is nil、编码失败日志 |
snmpget 返回 noSuchObject/无该 OID |
snmp 组件(接口未注册) | URI/OID/接口名/模式校验失败日志 |
snmpset 返回 noAccess(6) |
snmp 组件 | 接口 Mode 为 Readonly,或列 Access 为 Readonly |
snmpset 返回 wrongType(7)/badValue(3) |
snmp 组件 | 请求类型与配置 Type 不匹配、转换失败 |
表接口 snmpwalk 无数据 |
snmp 组件或资源协作接口 | the count of sequence is invalid、get the instance ... failed |
| SNMP 访问被拒(用户锁定) | snmp 组件(user_login_lock.c)或账号服务 |
[user login lock] 日志 |
| 组件加载失败 | snmp 组件 | load the lua lib failed 等初始化日志 |
5.2 错误码
net-snmp 标准错误状态码,
message.lua与 C 层使用
| 错误码 | 名称 | 含义 |
|---|---|---|
| 0 | SNMP_ERR_NOERROR |
成功 |
| 3 | SNMP_ERR_BADVALUE |
值非法(类型/值转换失败) |
| 5 | SNMP_ERR_GENERR |
一般性错误(映射失败、响应体 nil/编码失败) |
| 6 | SNMP_ERR_NOACCESS |
无访问权限(只读属性被 Set) |
| 7 | SNMP_ERR_WRONGTYPE |
类型错误(请求数据类型与配置不符) |
5.3 调试/复现方法:
用 snmpwalk/snmpget/snmpset 复现 → 查看模块名 snmp 的日志,判断是「注册阶段跳过」还是「运行阶段失败」→ 核查映射配置校验规则、接口 Mode/列 Access、表接口主键配置、资源协作接口对象状态。
6. 常见问题解答
Q1:snmpget 访问某 OID 返回 genErr(5),如何定位?
- **一句话答案:**先看组件日志(模块名
snmp)有无「映射失败/响应体 nil/编码失败」类错误。 - 根因:
genErr(5)是映射失败、响应体为 null、响应体编码失败等情况统一返回的错误码。 - **解决方案:**核查映射配置与资源协作接口对象,定位日志中
create the mapper ... failed、The route configuration is missing、the response body is nil等关键信息。
Q2:新增 SNMP 接口需要改代码吗?
- **一句话答案:**不需要,在映射配置目录新增 URI 配置即可。
- **根因:**组件经
route_mapper读取interface_config/<product>/mapping_config动态注册。 - **解决方案:**按 3 示例配置,注意 URI 校验规则,重启 snmpd 后用
snmpwalk/snmpget验证。
Q3:表接口 snmpwalk 遍历不到数据?
- **一句话答案:**通常是
Sequence主键配置缺失/非法,或资源协作接口对象无数据。 - **根因:**表遍历依赖
Primary主键构建索引;实例获取失败或Count为 0 导致无数据。 - **解决方案:**检查
the count of sequence is invalid、get the instance ... failed日志,核对主键配置与资源协作接口对象。
Q4:snmpset 被拒(noAccess/wrongType/badValue)?
- **一句话答案:**分别对应「只读属性被写」「类型不符」「值非法」。
- 根因:
noAccess(6)由Mode/列Access为Readonly触发;wrongType(7)/badValue(3)由类型不匹配或转换失败触发。 - **解决方案:**核对接口
Mode、列Access、ReqBody/Sequence的Type与校验规则。
Q5:如何定制 OEM OID 前缀?
- **一句话答案:**通过替换关系配置
SnmpOemIdentifier设置。 - **根因:**默认 OEM 前缀
1.3.6.1.4.1.2011.2.235.1.1.,组件会替换为1.3.6.1.4.1.<替换值>.。 - **解决方案:**设置
SnmpOemIdentifier并保证映射配置 OID 前缀一致。