本文主要从新手的角度出发,描述如何基于 openUBMC 配置一个事件精细化告警。
openUBMC 自主设计了事件管理模块,在IPMI事件之外,为用户提供精细化告警能力。openUBMC通过该模块实现了灵活全面的事件管理能力,同时支持redfish上报,更加易于维护,推荐用户使用该模块进行事件管理。
下面详细介绍如何为传感器配置对应的 openUBMC 事件,根据是否依赖具体硬件可分为 CSR 配置告警和事件 RPC 告警两种方式。
事件的静态配置
配置说明
不论是通过CSR告警还是通过RPC告警,都需要配置静态信息。静态配置可以理解为告警的固定信息,一般不支持更改,包括了事件的定义和描述信息:
- 事件的定义,如:事件码、严重等级、上报通道等
- 事件的描述,默认支持中英文,如:事件描述模板,事件建议模板,事件的影响,事件的原因等
二者涉及到的字段和含义如下表:
字段名 | 解释 |
---|---|
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
中配置,下面给出配置示例。
- vpd/vendor/event_def.json 事件定义配置
注意事项
添加告警后需要更新版本号,如
1.0.2
EventDefinition
与EventDescription
需要配套,通过EventKeyId
匹配如果描述中需要用到
openUBMC
字段请配置为{BMC}
,此字段会自动替换json"Version": "1.0.1", "EventDefinition": [ { "EventKeyId": "Disk.DiskCEHardFailure", -- 如DiskCEHardFailure "EventCode": "0x0200001F", "ReportChannel": 65535, "OldEventCode": "", "EventType": 0, "LifeCycleId": 1, "DeassertFlag": 1, "SeverityId": 0, "ActionId": 0, "EventName": "DiskCEHardFailure" } ], "EventDescription": [ { "EventKeyId": "Disk.DiskCEHardFailure", "Suggestion": { "En": "1. Perform maintenance according to the maintenance plan as soon as possible. Power off the server...", "Zh": "1、尽快安排计划性维护,下电后检查该部件与其插槽是否存在损坏或接触不良现象。@#AB;2、更换该部件并进一步观察。" }, "Description": { "En": "The %1 disk %2 health status degradation detected by PFAE.", "Zh": "%1硬盘 %2 健康状态降级。" }, "Influence": { "En": "", "Zh": "" }, "Cause": { "En": "", "Zh": "" } } ]
vendor/Huawei/Server/Kunpeng/openUBMC/event/eventDefList.txt 告警清单
此清单表征要从event_def.json
中加载哪些告警,添加EventKeyId
即可
Disk.DiskCEHardFailure
事件的CSR配置
CSR配置可以理解为告警的动态信息,这些信息都是支持配置的,即可更改的。对于有具体的硬件形态的事件/告警,通过CSR配置即可实现。
在openUBMC中,电源事件和其他事件的CSR配置方式不同,下面将分别介绍普通事件和电源事件的CSR配置方式。CSR 配置语法可参考《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配置样例
配置关键点:
event对象配置在哪里?CSR是严格按照硬件的拓扑结构配置的,事件的配置均归属于当前事件所属硬件的CSR上,比如:PCIE卡上的告警就在PCIE卡所在的SR文件中,可以在 vpd 仓库中找到。
bashCSR ├── PCIECard │ ├── FRU对象 │ │ └── PCIEFru │ ├── Component对象 │ │ ├── Com1822 │ │ ├── ComPort1 │ │ └── ComPort2 │ ├── ThresholdSensor对象 │ │ └── 1822 Core Temperature │ └── Event对象 │ └── 1822 Core Temperature Major
配置Event时,应先查找有没有对应的Compoent对象,没有就需要配置一个。正常情况下只需要配置一个Event对象即可,因为Component类归属在frudata下,一般已由Frudata配置好。
注意
由于platform中的对象可以跨文件引用,所以对于理论上唯一的对象应当复用,比如Component_BMC、Component_System,避免重复定义产生冗余代码。
下列实例会注册一个风扇板上的 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上不会显示此信息),取自关联的 Component 上的 SerialNumber/PartNumber,若取值为空则不显示。
SN/BN的处理逻辑为:不同的部件类型取的数据源不同。Component的SN/BN开放配置,由组件自己决定如何显示,即通过SR语法决定取值,为空则不显示;Event内不限制SN/BN的取值逻辑,支持增删改,可以根据SR适配不同产品。
如何产生/恢复告警
生成告警
简单来说,关注表达式
Reading OperatorId Condition
即可。在上述例子中,
OperatorId
为6即代表运算符是使用的不等于运算;Reading != Condition
即 1 != 0,表达式如果返回true则达到告警门限,为false则代表Reading虽然更新了,但没触发到门限。上述例子表示Reading变为1,产生告警。恢复告警
以下为简单场景举例,在不考虑恢复策略的情况下,
Reading + Hysteresis == Condition
即1 + 0 == 2
返回false则恢复告警,读数变化后,不再满足触发门限的条件便自动恢复。
告警条件完全由配置决定,是直接用扫描到的值还是自行定义一个值都可以。
如何配置防抖
在实际场景下,若监测值经常变化,可能会多次触发告警误报,openUBMC对这类经常变化的值设计了防抖策略,配置在Scanner对象的Debounce属性中,可以配置MidAvg、Median、Cont、ContBin、None五种类型的防抖策略。
防抖类型 | 含义 | 参数 | 配置实例 |
---|---|---|---|
MidAvg | 均值平均 | WindowSize:窗口大小 DefaultValue:默认值 IsSigned:是否有符号数 | "Median": { "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 } |
电源事件的配置
电源事件 是 openUBMC 提供的一种特殊的事件类型,针对 某一类型但是需要不同门限以及LED显示码 的一簇事件的集合。除了普通事件的属性之外,电源事件还包括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 进行条件比较。
事件配置调测
事件配置可以通过出包 + 上板调测的方式来验证是否成功。参考《适配一款硬件》 完成组件构建、整包构建、整包升级后,上环境查看事件对象是否挂上,可手动构造错误值触发事件告警。具体做法如下:
> busctl --user tree bmc.kepler.event
> busctl --user introspect bmc.kepler.event /bmc/kepler/Systems/1/Events
事件 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
为例添加一个事件
> 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调用事件接口
在代码中根据实际情况操作软件告警是一种常见场景,下面介绍通过RPC调用事件接口的方法。
在业务代码中,如果需要调用事件接口,需要订阅接口后用框架提供的client实现,具体实例为:
-- 获取当前告警
local amx, rsp = rpc_client.GetAlarmList_PACKED(1):unpack()
-- 添加告警事件
local context = require 'mc.context'
local event_obj = rpc_client.GetEventsObects()
local params = {
'ComponentName': 'BMC',
'State': 'true',
'EventKeyId': 'BMC.InsecureCryptographicAlgorithm',
...
}
local record = event_obj:AddEvent_PACKED(context.new(), params):unpack()
总结
以上是事件定制的实操流程说明,开发者可以据此配置一个精细化事件告警。