告警配置机制介绍
整体介绍
目前,openUBMC 支持两类事件告警:传感器事件告警与系统事件告警。传感器事件告警遵循 IPMI 协议规范,可进一步划分为阈值传感器告警与离散传感器告警,并可通过 IPMI 协议中的sel命令进行解析。此外,为适应日益丰富和复杂的告警需求,openUBMC 还自主设计并实现了一套更为精细化的告警机制——系统事件告警。该机制不依赖于 IPMI 协议,支持通过 Redfish 接口上报事件,具备更高的灵活性与全面性。
需特别说明的是,执行 IPMI 的sel clear命令将同时清除 Web 界面中显示的传感器事件告警与系统事件告警。
有关传感器事件告警的配置机制,可参阅《传感器适配指导》文档,本文则主要围绕 openUBMC 自主研发的系统事件告警的配置机制展开说明。
告警流程
根据是否依赖于具体硬件,系统事件告警可分为 CSR 配置告警与事件 RPC 告警两种方式:前者基于硬件触发,后者则由软件事件产生。以下为 openUBMC 内部对于硬件和系统状态进行监控、上报和处理的内部逻辑流程。
告警配置
告警配置包括静态配置和CSR配置两类。
静态配置
所有系统事件告警,无论其触发方式为 CSR 或 RPC,都必须依赖预设的静态配置信息。静态配置在 vpd 仓库中进行配置,该配置定义了告警的固定属性,原则上不可更改,具体包含以下两个条目:
- 事件定义:包含事件码、严重等级、上报通道等关键属性。
- 事件描述:默认支持中英文,具体包括事件描述模板、修复建议模板、事件影响及事件产生原因等。
静态配置涉及到的字段和含义如下表:
| 字段名 | 解释 |
|---|---|
| EventKeyId | 事件定义 |
| EventName | 事件名称(需保证该名称在整个vpd仓库中唯一,不要和其他告警重名,否则影响事件订阅) |
| EventType | 事件类型0:系统事件 1:维护事件 2:运行事件 |
| SeverityId | 严重等级0:Normal 1: Minor 2: Major 3:Critical |
| EventCode | 事件码 |
| OldEventCode | 旧事件码 |
| ActionId | 事件动作0: No Action 1: Power off the host 2: Restart the host 3:Power cycle the host |
| LifeCycleId | 事件生命周期 |
| ReportChannel | 事件上报通道掩码,其中bit6表征是否记录到告警(1:是 0:否),如果不支持则只会体现在历史记录中,不会出现在当前告警页面,用于支持特殊场景用法,正常情况都取值1即可 |
| Description | 事件描述 (对外展示的事件描述会清除连续空格以及逗号、分号、句号、顿号前的空格) |
| Suggestion | 事件建议 |
| Influence | 事件影响 |
| Cause | 事件原因 |
| DeassertFlag | 是否可以恢复(此属性容易误解,实际含义可以理解为是否需要Deassert事件(0:不需要,1:需要),也就是在告警恢复的时候,是否产生一条历史记录,容易被误解为告警不能恢复) |
新增告警配置举例
在vpd/vendor/event_def.json添加新的告警类型,如内存训练失败。
注意事项
EventDefinition与EventDescription需要配套,通过EventKeyId匹配- 如果描述中需要用到 openUBMC 字段请配置为
{BMC},此字段会自动替换 - 添加新的告警类型后,需要更新版本号,如原来版本号为
1.0.43,则更新为1.0.44
"Version": "1.0.44",
"EventDefinition": [
{
"EventCode": "0x01000019",
"ReportChannel": 65535,
"OldEventCode": "",
"EventType": 0,
"LifeCycleId": 0,
"DeassertFlag": 1,
"EventKeyId": "Memory.MemoryTrainFailure",
"SeverityId": 3,
"ActionId": 0,
"EventName": "MemoryTrainFailure"
}
],
"EventDescription": [
{
"Suggestion": {
"En": "1. Power off the server and check whether there is damage or poor contact between the component and its slot.@#AB;2. Replace the component and check for alarms.",
"Zh": "1、下电后检查该部件与其插槽是否存在损坏或接触不良现象。@#AB;2、更换该部件并进一步观察。"
},
"EventKeyId": "Memory.MemoryTrainFailure",
"Description": {
"En": "%1 %2 %3 triggered an uncorrectable error, %4.",
"Zh": "%1 %2 DIMM%3已触发内存训练失败,%4。"
},
"Influence": {
"En": "The system failed to start up.",
"Zh": "可能导致系统无法正常启动。"
},
"Cause": {
"En": "1. The memory module is faulty.@#AB;2. The slot of the memory module on the mainboard is faulty.",
"Zh": "1、内存故障。@#AB;2、主板内存条槽位故障。"
}
}
]更新机型下的告警清单。以 openUBMC 为例,在新增告警类型后,需要更新vpd/vendor/Huawei/Server/Kunpeng/openUBMC/event/eventDefList.txt 告警清单。 此清单表示要从event_def.json中加载哪些类型告警,添加EventKeyId即可。本例则增加Memory.MemoryTrainFailure类型告警。
事件的CSR配置
CSR配置可视为告警的动态信息,这些信息均支持灵活配置与修改。对于具备具体硬件形态的事件或告警,可通过CSR配置直接实现。 在openUBMC中,电源事件与普通事件的CSR配置方式存在差异,下文将分别对普通事件及电源事件的配置方式进行说明。
普通事件 CSR 配置
普通事件涉及到的字段和含义如下表:
| 字段名 | 解释 |
|---|---|
| EventKeyId | 事件标识,用于匹配事件的静态配置 |
| Reading | 告警读数,一般配置为对其他值的同步语法,普通的事件比如温度可以直接拿来当做读数,其他类型的比如证书过期,可以是0和1来代替 |
| Condition | 告警门限值 |
| OperatorId | 判断符号,有下列八种判断方式:1:小于 2:小于等于 3:大于 4:大于等于 5:等于 6:不等于 7:上升沿0->1产生,1->0恢复 8:下降沿1->0产生,0->1恢复 |
| Hysteresis | 迟滞量在恢复告警时使用,产生告警时不会使用,如果为0意味着立即恢复 可以理解为误差 |
| Enabled | 事件使能状态,或者称为屏蔽状态 |
| Component | 关联的Component对象,Component定义可参考《frudata》 |
| DescArgx/SuggArgx | (选配) 事件的描述/建议参数,用于格式化参数,仅支持字符串格式,最多10个(通过SR表达式format进行配置) |
| AdditionalInfo | (选配)事件的附加信息,即第X个动态参数,也可以是‘1,2’指向多个,用于FD上报时区分不同事件,比如完全相同的告警仅槽位不同,一般用槽位做区分,新增的告警需要特别注意是否需要配置本字段 |
| LedFaultCode | (选配) Led错误码,可为固定值或动态值,x取Component中的Instance填充部分 |
| InvalidReadingIgnore | (选配)是否忽略无效值,1:开启 0 : 关闭,开启后读值如果等于InvalidReading则忽略 |
| InvalidReading | (选配) 需要忽略的无效值 |
CSR 配置样例
CSR 严格遵循硬件的拓扑结构进行配置,所有由硬件触发的系统事件,其配置均归属于其对应硬件实体的 CSR。例如 CLU(风扇板)产生的告警,可参考 vpd/vendor/Huawei/TianChi/CLU 目录下的 sr 文件。
在配置 Event 时,应先检查是否存在对应的
Component对象。若不存在,则需先行配置该Component对象。通常情况下,仅需配置一个 Event 对象即可,因为Component类归属于 frudata 范畴,通常已由 Frudata 完成配置。注意: 由于
platform.sr中的对象可以跨文件引用,因此对于理论上应唯一存在的对象(如Component_ComBMC、Component_ComSystem),应当复用现有定义,避免重复定义导致代码冗余。 下列实例会注册一个风扇板上的 FanSpeedDeviation 事件(不配置的字段会使用该字段的默认值,所以按需配置即可)。
{
"Objects": {
"Event_Fan1FStatus": { // Event是类名,所有Event类都会分发到事件模块处理,Fan1FStatus 是名称,**单个文件中对象名应唯一**,完整的资源名称由自发现机制根据 SR 文件拼接,比如拼接成 Event_Fan1FStatus_00
"EventKeyId": "Fan.FanSpeedDeviation",
"Reading": "<=/Fan_1.FrontStatus",
"Condition": 0,
"OperatorId": 6,
"Enabled": true,
"DescArg1": "#/Fan_1.FanId",
"DescArg2": "front",
"Component": "#/Component_Fan1",
"AdditionalInfo" : "1,2",
"LedFaultCode": "F01"
},
// 除Reading可以配置同步语法外,其它属性均需使用引用语法,因为同步语法存在轮询时间间隔,会概率性导致告警描述中信息丢失或值未更新。
// 在现有风扇对象的基础上配置Event_Fan1FStatus事件对象,下面是依赖的对象
"Component_Fan1": {
"FruId": 255,
"Instance": "<=/Fan_1.FanId",
"Type": 4,
"Name": "Fan1",
"Presence": "<=/Fan_1.FrontPresence",
"Health": 0,
"PowerState": 1,
"UniqueId": "N/A",
"Manufacturer": "",
"GroupId": 1,
"Location": "<=/Component_CLU.Name",
"NodeId": "0"
},
"Fan_1": {
"FanId": 1,
"Slot": 1,
"Coefficient": 1,
"FrontPresence": "<=/Scanner_Fan1_Presence.Value",
"RearPresence": "<=/Scanner_Fan1_Presence.Value",
"FrontSpeed": "<=/Scanner_Fan1_FSpeed.Value",
"RearSpeed": "<=/Scanner_Fan1_RSpeed.Value",
"HardwarePWM": "#/Accessor_Fan1_PWM.Value",
"SystemId": 1,
"FrontStatus": 0,
"RearStatus": 0,
"MaxSupportedPWM": 255,
"IdentifySpeedLevel": 35,
"Position": "CLU",
"PowerGood": "#/Scanner_PowerGood.Value"
},
"Component_CLU": {
"FruId": 255,
"Instance": 255,
"Type": 196,
"Name": "CLU${Slot}",
"Presence": 1,
"Health": 0,
"PowerState": 1,
"BoardId": 65535,
"UniqueId": "N/A",
"Manufacturer": "",
"GroupId": 1,
"Location": "chassis"
},
"Scanner_Fan1_FSpeed": {
"Chip": "#/Smc_FanBoardSMC",
"Offset": 402657025,
"Size": 4,
"Mask": 4294901760,
"Type": 0,
"Period": 1000,
"Debounce": "None",
"Value": 0
},
"Scanner_Fan1_RSpeed": {
"Chip": "#/Smc_FanBoardSMC",
"Offset": 402657025,
"Size": 4,
"Mask": 65535,
"Type": 0,
"Period": 1000,
"Debounce": "None",
"Value": 0
},
"Accessor_Fan1_PWM": {
"Chip": "#/Smc_FanBoardSMC",
"Offset": 402657281,
"Size": 1,
"Mask": 255,
"Type": 0,
"Value": 0
},
}
}注意
若配置了DescArgx/SuggArgx参数,系统将在事件描述信息后附加序列号(SN)或部件号(BN)信息(该信息不会在 OMRP 界面中显示)。该 SN/BN 数据来源于关联Component对象中的SerialNumber与PartNumber字段,若对应字段取值为空,则不显示相关信息。
SN/BN 的具体处理逻辑如下:不同部件类型采用不同的数据源。Component中的 SN/BN 字段支持开放配置,由组件自己决定如何显示,即通过 SR 语法决定具体取值,若配置为空则不予显示;Event 内不限制 SN/BN 的取值逻辑,支持增删改操作,从而可通过 SR 文件适配不同产品的需求。
告警的触发与恢复机制
告警的触发与恢复逻辑,核心在于对条件表达式的判定。
告警触发
告警的触发条件可简化为一个核心表达式:
Reading OperatorId Condition。- OperatorId:定义比较运算符。例如,当其值为 6 时,通过查看普通事件 CSR 配置表,可知 6 代表“不等于”(!=)运算。
- 表达式判定:当
Reading通过所配置的比较运算符OperatorId与Condition比较后,如果结果为 true 时,则达到告警阈值,系统将产生告警;若为 false,则表明Reading虽已更新,但未满足告警条件。
示例:
在上述 FanSpeedDeviation 例子中,则需判断表达式
Reading != Condition的逻辑运算结果,即1 != 0,表达式返回true则产生告警。告警恢复
告警的恢复同样基于条件表达式的判定。在不考虑复杂恢复策略的简单场景下,当读数变化后,系统会重新计算条件表达式,若表达式返回
false,则恢复告警。核心要点:告警条件完全由配置驱动。无论
Reading是直接来自设备扫描的原始值,还是经由逻辑处理后的派生值,均可用于告警判定。
告警防抖策略配置
在实际应用中,监测数值的频繁波动易导致告警反复触发,产生误报。为提升告警的准确性与有效性,openUBMC 设计了防抖机制,通过配置Scanner对象的Debounce属性实现。当前支持配置MidAvg、Median、Cont、ContBin、None五种类型的防抖策略。
| 防抖类型 | 含义 | 参数 | 配置实例 |
|---|---|---|---|
| MidAvg | 均值平均 | WindowSize:窗口大小 DefaultValue:默认值 IsSigned:是否有符号数 | "MidAvg": { "WindowSize": 6, "DefaultValue": 11 "IsSigned": true } |
| Median | 中值滤波 | WindowSize:窗口大小 DefaultValue:默认值 | "Median": { "WindowSize": 6, "DefaultValue": 11 } |
| Cont | 持续一致 | Num:防抖次数 DefaultValue:默认值 | "Cont": { "Num": 6, "DefaultValue": 11 } |
| ContBin | 二值持续一致 | NumH:高电平防抖次数 NumL:低电平防抖次数 DefaultValue:默认值 | "ContBin": { "NumH": 6, "NumL": 6, "DefaultValue": 11 } |
| None | 无防抖 | DefaultValue:默认值 | "None": { "DefaultValue": 11 } |
适用原则:
- 温度监控:主要使用 Median 和 MidAvg 类防抖
- 状态监控:主要使用 Cont 和 ContBin 类防抖
- 电压监控:主要使用 MidAvg 类防抖
- 故障检测:主要使用 ContBin 类防抖,根据故障严重程度选择不同的防抖参数
详细可参考CSR硬件监控防抖机制。
电源事件配置
电源事件是 openUBMC 系统中一类特殊的事件类型,针对某一类型、但需设定不同阈值与对应 LED 显示码的一组关联事件进行统一管理。除具备普通事件的通用属性外,电源事件还包含一个专用的 Mappings 字段,用于定义该事件簇中各个子事件的具体行为。
Mappings 字段包含以下关键子项:
Mappings.Reading
表示该电源事件簇中某一子事件的触发条件阈值(Condition)。当监测读数达到此值时,即会生成相应事件。
Mappings.LedFaultCode
表示电源事件簇中的一个事件的
LedFaultCode,只要事件生成则需要显示的 LED 编号。Mappings.DescArgs
表示电源事件簇中的一个事件的 DescArgs,以字符串数组形式提供,此字段最多支持 10 个元素,用于在事件描述中填充可变信息。
电源事件CSR配置示例
下列实例注册了一个电源的事件,未删减内容参见 vpd/vendor/Huawei/TianChi/BCU/PsEvent_BC83AMDA_0_soft.sr。
{
"Objects": {
"PowerEvent_BCUPwrFaultMntr": {
"EventKeyId": "System.SystemPowerFailure",
"Component": "#/Component_ComSystem",
"Reading": "<=/Scanner_BCUPwrSigDrop.Value",
"AdditionalInfo": "2",
"Mappings": [
{
"Reading": 136,
"LedFaultCode": "U10",
"DescArgs": [
"",
"BCU_V_VCC_12V0_1"
]
},
{
"Reading": 137,
"LedFaultCode": "U10",
"DescArgs": [
"",
"BCU_V_VCC_12V0_2"
]
},
{
"Reading": 138,
"LedFaultCode": "U10",
"DescArgs": [
"",
"BCU_V_VCC_12V0_3"
]
},
...
{
"Reading": 182,
"LedFaultCode": "U00",
"DescArgs": [
"",
"BCU_V_STBY_1V8"
]
}
]
}
}
}电源告警的产生与恢复机制
电源事件的告警产生与恢复逻辑与普通事件基本一致,其主要区别在于:电源事件需同时对Reading和Mappings.Reading字段进行比较,以判断是否满足告警触发或恢复的阈值条件。
事件 RPC 告警
配置说明
软件类事件配置适用于系统级或软件级告警场景,该类告警通常难以在 CSR中准确描述。此类事件一般由组件在运行过程中根据实时运行数据判断是否应触发告警或记录事件。因此,对于具有明确硬件形态的事件或告警,不建议配置为软件类事件。
接口使用约束
- 生命周期管理责任:软件类告警的完整生命周期(包括告警的产生与恢复)由对应的组件负责管理。无论是否定义了
Deassert事件或告警恢复事件码,必须要有恢复的动作。 - 组件匹配规则:软件类告警根据
ComponentName与SubjectType匹配首个符合条件的Component对象。同一类型部件的ComponentName应保持全局唯一。 - 重复操作限制:同一事件不允许重复执行 Assert 或 Deassert 操作。
其他特性说明
- 事件唯一性标识:通过
ComponentName、EventKeyId、MessageArgs唯一确定一个软件告警事件; - 持久化与状态维护:软件告警具有复位持久化特性。因此,组件需自行维护告警的当前状态,避免重复添加报错。
- 接口可用性窗口:软件告警对外接口提供的窗口期受 SR 分发影响,若组件在服务启动初期即有调用需求,必须引入重试机制并配合 pcall 等防护措施,以应对服务尚未就绪的情况。
配置示例
以下示例展示了 network_adapter 组件对“链路异常”事件的软件告警处理逻辑。开发者可参考函数 check_oam_lost_link_state_alarm的实现,理解事件 RPC 告警的使用方式。
示例代码位于:network_adapter/src/lualib/event/event_mgmt.lua
function event_mgmt:add_event(params)
local event_obj
client:ForeachEventsObjects(function(o)
event_obj = o -- 此对象唯一
end)
if not event_obj then
log:error('get events object failed')
return
end
local ok, res = pcall(function ()
return event_obj:AddEvent_PACKED(ctx.new(), params):unpack()
end)
if not ok then
log:error('add events failed, %s', res)
return false
end
log:notice('add event successfully, record id [%s]', res)
return true
end
-- 链路异常告警
function event_mgmt:check_oam_lost_link_state_alarm(state, device_name, port_id)
local args = json.encode({device_name, '', 'Port ' .. (port_id + 1)})
local assert = state == 1 -- 0:未告警 1:告警
local alarm_state = alarm_states[args]
if not assert == not alarm_state then -- 默认nil,此处取反
return
end
local params = {
{'ComponentName', 'Port' .. (port_id + 1)}, -- port资源协作接口id从0开始,部件id从1开始
{'State', assert and 'true' or 'false'},
{'EventKeyId', 'Port.PortOAMLostLink'},
{'MessageArgs', args},
{'SystemId', ''},
{'ManagerId', ''},
{'ChassisId', ''},
{'NodeId', ''}
}
local is_ok = self:add_event(params)
if not is_ok then
return false
end
-- 更新本地告警信息
alarm_states[args] = assert
self:update_alarm_msg(assert, args, '') -- 动态参数具备唯一性所以可以直接做key,不需要value值
return true
end接口调用演示
软件告警可以通过接口调用的方式添加,在资源协作接口的/bmc/kepler/Systems/:SystemId/Events 路径下,调用bmc.kepler.Systems.Events 接口的AddEvent 方法,接口参数如下:
| 参数 | 含义 | 说明(字符串类型) |
|---|---|---|
| Component | 事件主体名称 | 必选,事件关联的部件Component的名称 |
| State | 事件状态 | 必选:true/false |
| EventKeyId | 事件标识 | 必选:同静态配置 |
| SubjectType | 事件主体类型 | 可选,不传入则根据事件码高位匹配 |
| SuggestionArgs | 事件建议参数 | 可选,格式需要通过json.encode转换 |
| MessageArgs | 事件描述参数 | 必选,格式需要通过json.encode转换,不涉及也需要传空表 |
| SystemId | 事件归属SystemId | 见下方说明 |
| ManagerId | 事件归属ManagerId | 见下方说明 |
| ChassisId | 事件归属ChassisId | 见下方说明 |
| NodeId | 事件对应的对象(NodeId) | 见下方说明 |
| LeaFaultCode | Led灯故障码 | 可选 |
关于SystemId、ManagerId、ChassisId、NodeId的取值说明:
- 如果事件源来自资源协作接口对象,则SystemId、ManagerId、ChassisId、NodeId需同步;
- 如果无事件源则按资源分类,SystemId、ManagerId、ChassisId三选一填写,NodeId可以为空。
调用实例
- 以下为通过
busctl工具调用AddEvent添加一个事件
busctl --user call bmc.kepler.event /bmc/kepler/Systems/1/Events bmc.kepler.Systems.Events AddEvent 'a{ss}a(ss)' 3 Interface cli UserName Administrator ClientAddr 127.0.0.1 8 ComponentName 'BMC' State 'true' EventKeyId 'BMC.InsecureCryptographicAlgorithm' 'MessageArgs' '["test"]' 'SystemId' '1' 'ManagerId' '1' 'ChassisId' '1' 'NodeId' '1'RPC调用事件接口
在 openUBMC 系统中,基于实际业务状态触发告警是一项常见需求,下文将介绍通过 RPC 调用事件接口的具体实现方法。
在业务逻辑实现过程中,若需调用事件接口,需先在对应组件的 mds/service.json 文件中增加对接口bmc.kepler.Systems.Events的依赖,若不在此处配置则无法在代码中调用该接口的资源,随后通过 client.lua 提供的接口进行调用。以 bios 仓库为例:
- 在
required字段中增加接口依赖。 - client.lua 内为自动生成对应的资源协作接口方法调用、对象获取和信号订阅等函数。
- 在 bios/src/lualib/infrastructure/event.lua 中创建软件告警事件
local log = require 'mc.logging'
local context = require 'mc.context'
local client = require 'bios.client'
local skynet = require 'skynet'
local Event = {}
local STATE_ERROR = 'PropertyValueError: Incorrect value of property State.'
local function add_event(param)
local record
local event_obj = client:GetEventsEventsObject()
if not event_obj then
log:error('[bios]get events object failed')
return false
end
local ok, err = pcall(function ()
record = event_obj:AddEvent_PACKED(context.new(), param):unpack()
end)
if not ok then
local err_str = string.format('%s', err)
if err_str == STATE_ERROR then
log:info('[bios]event exist, no need add')
return true
end
log:notice('[bios] add event(%s) fail, err: %s', record, err)
return false
end
log:debug('[bios]add event(%s) successfully', record)
return true
end
local RETRY_TIMES = 2
local function retry_add_event(param)
for _ = 1, RETRY_TIMES do
local ok, ret = pcall(add_event, param)
if ok and ret then
return true
end
skynet.sleep(50)
end
return false
end
--- 产生软件告警
function Event.generate_event(msg)
local param = {}
for key, value in pairs(msg) do
param[#param + 1] = { key, value }
end
local res = retry_add_event(param)
if not res then
error('[bios]generate event fail')
end
end
return Event- 调用软件告警接口,生成告警