二次开发指南
sysSentry作为一款巡检任务管理框架,除了管理已提供的插件外,也支持对用户开发的插件进行管理,想要开发一款新的插件并通过sysSentry框架进行管理,需要实现以下能力:
- 编写插件管理配置文件
- 完成插件功能开发
本文档将详细介绍如何使用sysSentry提供的对外接口开发新的插件。
插件管理配置文件
用户编写的新插件可以通过sysSentry进行管理,为了达到此目的,需要用户为新插件增加对应的配置文件,该文件应放置在/etc/sysSentry/tasks/目录下,文件名为[插件名].mod,插件名仅支持字母、数字和下划线。所有配置项需写在[common]段下。假设插件名为test,配置文件参考:
oneshot类型任务的配置示例:
[root@openEuler ~]# cat /etc/sysSentry/tasks/test.mod
[common]
enabled=yes # 必选,是否加载插件
task_start=/usr/bin/test # 必选,插件启动命令
task_stop=pkill -f /usr/bin/test # 必选,插件停止命令
type=oneshot # 必选,插件任务类型
onstart=yes # 可选,插件随sysSentry服务启动时自动启动period类型任务的配置示例:
[root@openEuler ~]# cat /etc/sysSentry/tasks/test.mod
[common]
enabled=yes # 必选,是否加载插件
task_start=/usr/bin/test # 必选,插件启动命令
task_stop=kill $pid # 必选,插件停止命令
type=period # 必选,插件任务类型
interval=10 # 可选,周期执行间隔(仅period类型生效)
heartbeat_interval=120 # 可选,心跳检测间隔
onstart=yes # 可选,插件随sysSentry服务启动时自动启动配置项说明
各配置项的详细说明如下:
| 配置项 | 是否必选 | 取值范围 | 默认值(配置非法值时) | 说明 |
|---|---|---|---|---|
| enabled | 必选 | "yes" 或 "no" | 无(缺失或非法时配置文件加载失败) | 控制是否加载该插件。设置为"yes"时加载并启用插件,设置为"no"时加载但禁用插件。缺失此配置项或取值非法时,整个配置文件加载失败 |
| type | 必选 | "oneshot" 或 "period" | 无(缺失或非法时配置文件加载失败) | 插件任务类型。"oneshot"表示一次性任务,执行一次后退出;"period"表示周期性任务,按固定间隔反复执行。缺失此配置项或取值非法时,整个配置文件加载失败 |
| task_start | 必选 | 字符串(可执行命令路径) | 无(缺失时配置文件加载失败) | 插件启动命令,必须为可执行文件路径或命令。sysSentry通过subprocess.Popen执行该命令 |
| task_stop | 必选 | 字符串(停止命令) | 无(缺失时配置文件加载失败) | 插件停止命令。支持使用$pid作为占位符,运行时会被替换为插件进程的实际PID |
| task_pre | 可选 | 字符串(命令) | None(不执行前置命令) | 插件启动前执行的命令。多条命令以英文";"分隔,依次执行。任一命令执行失败(产生stderr输出)时,插件不会启动 |
| task_post | 可选 | 字符串(命令) | None(不执行后置命令) | 插件停止后执行的命令。多条命令以英文";"分隔,依次执行。命令执行失败仅记录警告日志,不影响插件停止流程 |
| interval | 可选(仅period类型生效) | 正整数(秒) | 3(使用inspect.conf中的全局配置值) | 周期执行间隔时间,单位为秒。仅period类型任务有效。缺失时使用/etc/sysSentry/inspect.conf中的全局Interval配置(默认3秒);配置为非整数或小于等于0时同样回退到全局默认值 注: interval并非严格遵守设置的时间,可能会长于预期时间。 |
| heartbeat_interval | 可选 | 正整数(秒),最小有效值为60 | -1(禁用心跳检测) | 心跳检测间隔时间,单位为秒。配置后插件需通过心跳socket发送心跳消息,超过该间隔未收到心跳则插件被标记为失败并发送SIGTERM信号终止。配置为非整数或小于等于0时禁用心跳检测(值为-1);配置值大于0但小于60时强制调整为60。 注: heartbeat_interval并非严格遵守设置的时间,可能会长于预期时间。 |
| onstart | 可选 | "yes" 或 "no" | oneshot类型:no(不随服务启动);period类型:yes(随服务启动) | 控制插件是否随sysSentry服务启动时自动启动。oneshot类型仅"yes"生效,其他值均视为不自动启动;period类型仅"no"不生效,其他值均视为自动启动 |
| env_file | 可选 | 字符串(文件路径) | ""(空字符串,不加载环境变量) | 插件环境变量文件路径。文件必须存在、为常规文件且可读,格式为KEY=VALUE(以#开头的行为注释,值可用双引号包裹)。路径不存在、非常规文件或不可读时,配置值回退为空字符串 |
| conflict | 可选 | "up"、"down" 或 "kill" | "up" | 冲突检测策略,控制当task_start命令对应的进程已在运行时的处理方式。"up":不做冲突检查,直接启动(默认行为);"down":检测到冲突则拒绝启动;"kill":先终止已运行的进程再启动。缺失或取值非法时默认为"up" |
| alarm_id | 可选 | 整数,1001~1128 | None(不启用告警功能) | 告警ID,用于关联xalarmd告警服务。取值必须在1001~1128范围内,已占用ID不可复用。缺失或取值非法时不启用告警功能 |
| alarm_clear_time | 可选 | 非负整数(秒) | 15(秒) | 告警老化清理时间,单位为秒。超过该时间的告警事件将被从告警列表中清除。仅在alarm_id有效时生效。缺失或取值非法(负数或非整数)时默认为15秒 |
说明:
- 缺失必选配置项(enabled、type、task_start、task_stop)或取值非法时,整个配置文件加载失败,插件不会被管理。
type字段不支持动态修改,通过sentryctl reload重新加载配置时,如果type值发生变化则重新加载失败。task_pre命令失败会阻止插件启动;task_post命令失败仅记录警告日志,不会阻止插件停止流程。
插件功能开发
插件使用限制
用户开发的新插件需满足如下要求:
- 插件开发语言无限制,但建议用户优选python或c语言开发,目前sysSentry仅提供python和c语言的二次开发接口;
- 所有插件必须为可执行文件;
- 所有插件必须包含停止命令,执行该命令可准确的停止插件任务而不影响系统上其他程序的运行;
获取采集数据(可选)
如果用户希望通过sentryCollector采集服务获取系统数据,请参考对接sentryCollector采集服务章节。
插件事件告警上报
用户可通过告警上报接口将插件的告警信息上报到xalarmd服务,并通过get_alarm接口查看告警内容:
[root@openEuler ~]# sentryctl get_alarm <插件名>sysSentry提供python与c两种语言的对外接口。
告警上报使用限制
告警上报仅支持告警id的范围为1001-1128共128种,已使用告警ID见下表。
常量 值 描述 MEMORY_ALARM_ID 1001 内存巡检告警ID SLOW_IO_ALARM_ID 1002 慢IO告警事件ID ALARM_REBOOT_EVENT 1003 BMC下电事件告警ID ALARM_REBOOT_ACK_EVENT 1004 BMC下电ack事件告警ID ALARM_OOM_EVENT 1005 OOM事件告警ID ALARM_OOM_ACK_EVENT 1006 OOM ack事件告警ID ALARM_PANIC_EVENT 1007 Panic事件告警ID ALARM_PANIC_ACK_EVENT 1008 Panic ack事件告警ID ALARM_KERNEL_REBOOT_EVENT 1009 内核重启事件告警ID ALARM_KERNEL_REBOOT_ACK_EVENT 1010 内核重启ack事件告警ID ALARM_UBUS_MEM_EVENT 1013 UBUS内存故障事件告警ID ALARM_LINK_EVENT 1016 链路(link)事件告警ID ALARM_RAS_SENTRY_EVENT 1015 BMC RAS告警事件告警ID SYSSENTRY_DOWN_ALARM_ID 1128 sysSentry服务停止告警告警ID 上面告警ID已被插件占用,用户新增插件不可复用,并且1011,1012和1014也不可使用(预留)。
告警上报最大支持8191个字符。
python实现插件告警上报
需要安装pysentry_notify软件包:
[root@openEuler ~]# yum install -y pysentry_notify接口 告警信息上报
| 接口 | xalarm_report(alarm_id, alarm_level, alarm_type, puc_paras) |
|---|---|
| 描述 | 巡检插件可以通过该接口上报告警信息到xalarmd服务 |
| 参数 | alarm_id -- 告警id,整数类型 alarm_level -- 告警级别,枚举类型,取值为:MINOR_ALM(一般告警)、MAJOR_ALM(严重告警)和CRITICAL_ALM(致命告警) alarm_type -- 告警类别,枚举类型,取值为:ALARM_TYPE_OCCUR(告警产生)和ALARM_TYPE_RECOVER(故障恢复) punc_params -- 告警描述信息,字符串类型 |
| 限制 | 1. 告警id限制取值范围为1001-1128。目前1001(内存巡检)、1002(慢IO检测)已被占用,不建议使用 2. 告警描述信息,最大长度为8191,在慢IO巡检中以json格式通信,具体json中各字段可以参考下面:慢IO上报json格式说明。 |
| 返回值 | 若上报告警成功,则返回值为True,否则返回值为False。 |
慢IO上报json格式说明:
| 参数名称 | 类型 | 取值说明 |
|---|---|---|
| device_name | 字符串 | 发生慢IO事件的故障盘设备名,例如"/dev/sda" |
| reason | 字符串 | 慢IO事件的故障原因,取值如下范围 disk_slow:可能由于盘侧响应码导致的慢IO;kernel_stack:可能由于内核栈导致的慢IO;high_press:可能由于业务压力大导致的慢IO |
| block_stack | 字符串列表 | 存在异常的IO调用栈列表,例如["bio","rq_driver"],取值范围如下:bio、 throtl、 wbt、 gettag、plug、deadline、 hctx、requeue、rq_driver |
| io_type | 字符串 | 出现慢IO的io类型,取值范围如下:read:读io出现慢io场景;write:写io出现慢io场景 |
| alarm_source | 字符串 | 告警来源插件,取值范围如下:avg_block_io:平均阈值检测插件告警;ai_block_io:ai阈值检测插件告警 |
| alarm_type | 字符串 | 出现慢IO告警的类型,取值范围如下:latency:时延数据超过阈值产生慢io告警;iodump:超时未完成的IO数量超过阈值产生慢IO告警 |
| details | JSON格式数据列表 | 详细的IO时延数据清单 |
示例:
from xalarm.sentry_notify import (
xalarm_report,
MAJOR_ALM,
ALARM_TYPE_OCCUR
)
ALARM_ID = 1002
ALARM_MSG = """
{
"alarm_info": {
"alarm_source": "avg_block_io",
"driver_name": "sda",
"io_type": "write",
"reason": "IO press",
"block_stack": "bio,wbt",
"alarm_type": "latency",
"details": {
"latency": "gettag: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], rq_driver: [0,0,0,0,0,437.9,0,0,0,0,517,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], bio: [0,0,0,0,0,521.1,0,0,0,0,557.8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], wbt: [0,0,0,0,0,0,8.5,0,0,0,0,12.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]",
"iodump": "gettag: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], rq_driver: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], bio: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], wbt: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]"
}
}
}
"""
if __name__ == "__main__":
ret = xalarm_report(ALARM_ID, MAJOR_ALM, ALARM_TYPE_OCCUR, ALARM_MSG)
if ret == -1:
print("send failed.")c语言实现插件告警上报
需要安装libxalarm软件包:
[root@openEuler ~]# yum install -y libxalarm开发环境还需要安装libxalarm-devel包(构建依赖,非运行依赖):
[root@openEuler ~]# yum install -y libxalarm-devel单向通信接口
单向通信接口,是向xalarmd服务上报事件告警的接口。
接口信息
| 接口 | int xalarm_Report(unsigned short usAlarmId, unsigned char ucAlarmLevel, unsigned char ucAlarmType, char *pucParas); |
|---|---|
| 描述 | sysSentry告警上报接口,用于向xalarmd上报需要转发的告警 |
| 参数 | usAlarmId -- 告警id usAlarmLevel -- 告警级别,枚举类型,取值为:MINOR_ALM(一般告警)、MAJOR_ALM(严重告警)或CRITICAL_ALM(致命告警) ucAlarmType -- 告警类别,取值范围为ALARM_TYPE_OCCUR(告警产生)或ALARM_TYPE_RECOVER(故障恢复) pucParas -- 告警描述信息,长度上限为8191个字符 |
| 限制 | 1. 告警id限制取值范围为1001-1128。目前1001(内存巡检)、1002(慢IO检测)已被占用,不建议使用 2. 告警描述信息最大长度为8191 |
| 返回值 | 返回0表示成功,失败返回-1 |
使用示例
[root@openEuler ~]# cat send_alarm.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <xalarm/register_xalarm.h>
#define ALARMID 1002
int main(int argc, char **argv)
{
int alarmId = ALARMID;
int level = MAJOR_ALM;
int type = ALARM_TYPE_OCCUR;
unsigned char *msg = "{\""
"alarm_info\": {"
"\"alarm_source\": \"avg_block_io\","
"\"driver_name\": \"sda\","
"\"io_type\": \"write\","
"\"reason\": \"IO press\","
"\"block_stack\": \"bio,wbt\","
"\"alarm_type\": \"latency\","
"\"details\": {"
"\"latency\": \"gettag: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], rq_driver: [0,0,0,0,0,437.9,0,0,0,0,517,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], bio: [0,0,0,0,0,521.1,0,0,0,0,557.8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], wbt: [0,0,0,0,0,0,8.5,0,0,0,0,12.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]\","
"\"iodump\": \"gettag: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], rq_driver: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], bio: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], wbt: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]\""
"}"
"}"
"}\0";
int ret = xalarm_Report(alarmId, level, type, msg);
if (ret == -1) {
printf("send failed.\n");
}
return 0;
}
[root@openEuler ~]# gcc send_alarm.c -o send_alarm -lxalarm双向通信接口
双向通信接口,是指既可以向xalarmd服务上报事件消息,又可以对消息进行回复。
接口信息
| 接口 | int xalarm_register_event(struct alarm_register** register_info, struct alarm_subscription_info id_filter); |
|---|---|
| 描述 | 用于向xalarmd服务创建socket连接,仅xalarm_get_event接口使用前需要注册,xalarm_report_event接口调用无需注册即可使用 |
| 参数 | register_info:订阅注册信息结构体指针地址 id_filter:订阅告警ID列表,为alarm_subscription_info结构体类型,可支持订阅1001-1128号告警,alarm_subscription_info结构体内有一个长度为128的数组id_filter,每个元素为int类型,表示需要订阅的id号,该结构体还有一个表示数组长度的成员len,设置长度为已需要订阅的告警id数量后才会生效。 |
| 返回值 | 返回值为整数类型,0表示注册成功,若返回值小于0,可能的原因如下: 1. 返回值若为-ENOTCONN,表示与xalarmd进程建立连接失败,建议稍后重新注册。 2.返回值若为-EINVAL,则可能是register_info传入空指针,或者id_filter不符合要求。 3. 返回值若为-ENOMEM,则是申请内存空间失败。 |
| 接口 | void xalarm_unregister_event(struct alarm_register **register_info); |
|---|---|
| 描述 | 用于解除对xalarmd服务的告警订阅信息,释放socket连接 |
| 参数 | register_info:alarm_register结构体类型,用于保存与xalarmd服务通信的socket连接以及订阅的告警id信息 |
| 返回值 | 无返回值,若解注册失败或者还未解注册程序异常退出,xalarmd服务也会定期清理无效的socket连接 |
| 接口 | int xalarm_report_event(unsigned short usAlarmId, char *pucParas, size_t len); |
|---|---|
| 描述 | 用户可以通过该接口上报告警信息到xalarmd服务 |
| 参数 | usAlarmId:告警id,整数类型。 pucParas:告警描述信息,字符串信息 len: 字符串pucParas的长度,不包含'\0' |
| 约束 | 告警id取值范围为1001-1128 告警描述信息最大长度为8191 |
| 返回值 | 若上报告警成功,则返回值为0,否则返回值小于0. 1.返回值为-EINVAL,则传入参数告警id超过取值范围或者pucParas为NULL以及超过8191的长度限制。 2.返回值为-ENOTCONN,socket连接失败,建议重新发送消息。 3.返回值为-ECOMM,发送socket消息失败,建议重新发送消息。 4.返回值为-ENODEV,则为socket创建失败。 |
| 接口 | int xalarm_get_event(struct alarm_msg *msg, struct alarm_register *register_info); |
|---|---|
| 描述 | 用于接收register_info订阅过的告警信息,可多次调用,每次调用会阻塞程序直到收到告警信息才会停止阻塞 |
| 参数 | msg:告警消息指针地址,使用之前建议使用memset清空内容。 register_info:alarm_register结构体类型,用于保存与xalarmd通信的socket连接以及订阅的告警id消息。 |
| 返回值 | 返回值为整数类型,若返回值为0表示获取消息成功(成果获取消息之后,该接口仍可重复使用),若返回值小于0,可能的原因如下: 1.返回值若为-EINVAL,msg或者register_info为空指针,建议尝试重新注册新的连接。 2.返回值若为-ENOTCONN、-EBADF,则应该是xalarmd服务异常,建议unregister之后再次执行register,get事件。 3.返回值若为-ENOMEM,则是申请内存空间失败。 4.返回值若为-EOPNOTSUPP,则表明未进行register操作或者register失败,建议重新register后再次进行get。 |
使用示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <xalarm/register_xalarm.h>
#define SLEEP_TIME 10
#define ID_LIST_LENGTH 1
#define TIME_UNIT_MILLISECONDS 1000
void process_alarm_info(struct alarm_msg* param)
{
int alarmid;
long long alarmtime;
char *pucParas;
alarmid = (param == NULL ? 0 : param->usAlarmId);
alarmtime = (param == NULL ? 0 : ((long long)param->AlarmTime.tv_sec) * TIME_UNIT_MILLISECONDS + (long long)(param->AlarmTime.tv_usec / TIME_UNIT_MILLISECONDS));
pucParas = (param == NULL ? NULL : param->pucParas);
printf("plugin notified with [alarmid:%d], [alarmtime: %lld ms], [msg:%s]\n", alarmid, alarmtime, pucParas);
return;
}
int main(int argc, char **argv)
{
struct alarm_msg* msg;
struct alarm_register* register_info;
struct alarm_subscription_info id_filter;
id_filter.id_list[0] = ALARM_REBOOT_EVENT;
id_filter.len = ID_LIST_LENGTH;
int ret = xalarm_register_event(®ister_info, id_filter);
if (ret < 0) {
perror("Failed to register xalarm\n");
return 1;
}
printf("Waiting for plugin msg ... \n");
ret = xalarm_get_event(msg, register_info);
if (ret < 0) {
perror("Failed to get msg\n");
return 1;
}
process_alarm_info(msg);
sleep(SLEEP_TIME);
ret = xalarm_report_event(ALARM_REBOOT_ACK_EVENT, "Reboot ACK", strlen("Reboot ACK"));
if (ret < 0) {
perror("Failed to send msg\n");
return 1;
}
xalarm_unregister_event(®ister_info);
return 0;
}插件事件告警订阅
sysSentry内置的告警转发子系统xalarmd通过收集巡检任务巡检过程中产生的告警信息,并将告警信息转发给订阅此类告警的用户。
python接口
告警订阅接口(回调模式)
xalarm_register
| 接口 | xalarm_register(callback: callable, id_filter: list) -> int |
|---|---|
| 描述 | 用于注册告警接收回调函数,订阅指定告警ID的告警信息 |
| 参数 | callback -- 告警回调函数,要求函数仅接收一个参数,用于接收告警信息对象 id_filter -- 告警ID过滤列表,长度必须为128,列表中每个元素为布尔值,表示是否订阅对应告警ID(索引0对应告警ID 1001,索引127对应告警ID 1128) |
| 限制 | 1. 告警ID过滤列表长度必须为128 2. 回调函数必须仅接收一个参数 3. 当前仅支持单例注册,即同一进程只能注册一次告警回调 |
| 返回值 | 注册成功返回0,注册失败返回-1 |
xalarm_unregister
| 接口 | xalarm_unregister(clientId: int) -> None |
|---|---|
| 描述 | 用于注销已注册的告警回调函数,停止接收告警信息 |
| 参数 | clientId -- 客户端ID,整数类型 |
| 限制 | clientId无用, 目前仅支持单个客户端注册/解注册,因此clientId目前只能是0 |
| 返回值 | 无返回值 |
xalarm_upgrade
| 接口 | xalarm_upgrade(clientId: int, id_filter: list) -> bool |
|---|---|
| 描述 | 用于更新已注册的告警ID订阅列表,动态调整订阅的告警类型 |
| 参数 | clientId -- 客户端ID,整数类型 id_filter -- 新的告警ID过滤列表,长度必须为128 |
| 限制 | 1. clientId目前只能是0 2. 告警ID过滤列表长度必须为128 3. 必须先完成告警注册才能进行订阅更新 |
| 返回值 | 更新成功返回True,更新失败返回False |
xalarm_getid
| 接口 | xalarm_getid(alarm_info: Xalarm) -> int |
|---|---|
| 描述 | 从告警信息对象中获取告警ID |
| 参数 | alarm_info -- 告警信息对象,类型为Xalarm |
| 返回值 | 返回告警ID,整数类型。若alarm_info为空则返回0 |
xalarm_getlevel
| 接口 | xalarm_getlevel(alarm_info: Xalarm) -> int |
|---|---|
| 描述 | 从告警信息对象中获取告警级别 |
| 参数 | alarm_info -- 告警信息对象,类型为Xalarm |
| 返回值 | 返回告警级别,整数类型,取值范围为:MINOR_ALM(一般告警)、MAJOR_ALM(严重告警)或CRITICAL_ALM(致命告警)。若alarm_info为空则返回0 |
xalarm_gettype
| 接口 | xalarm_gettype(alarm_info: Xalarm) -> int |
|---|---|
| 描述 | 从告警信息对象中获取告警类型 |
| 参数 | alarm_info -- 告警信息对象,类型为Xalarm |
| 返回值 | 返回告警类型,整数类型,取值范围为:ALARM_TYPE_OCCUR(告警产生)或ALARM_TYPE_RECOVER(故障恢复)。若alarm_info为空则返回0 |
xalarm_gettime
| 接口 | xalarm_gettime(alarm_info: Xalarm) -> int |
|---|---|
| 描述 | 从告警信息对象中获取告警产生时间 |
| 参数 | alarm_info -- 告警信息对象,类型为Xalarm |
| 返回值 | 返回告警时间,整数类型,单位为毫秒。若alarm_info为空则返回0 |
xalarm_getdesc
| 接口 | xalarm_getdesc(alarm_info: Xalarm) -> str |
|---|---|
| 描述 | 从告警信息对象中获取告警描述信息 |
| 参数 | alarm_info -- 告警信息对象,类型为Xalarm |
| 返回值 | 返回告警描述信息,字符串类型。若alarm_info为空或解码失败则返回None |
使用示例
以下示例展示如何注册告警回调函数并处理接收到的告警信息:
import time
from xalarm.register_xalarm import (
xalarm_register,
xalarm_unregister,
xalarm_getid,
xalarm_getlevel,
xalarm_gettype,
xalarm_gettime,
xalarm_getdesc
)
ALARM_RAS_SENTRY_EVENT = 1015
def alarm_handler(alarm_info):
alarm_id = xalarm_getid(alarm_info)
alarm_level = xalarm_getlevel(alarm_info)
alarm_type = xalarm_gettype(alarm_info)
alarm_time = xalarm_gettime(alarm_info)
alarm_desc = xalarm_getdesc(alarm_info)
print(f"收到告警:")
print(f" ID: {alarm_id}")
print(f" 级别: {alarm_level}")
print(f" 类型: {alarm_type}")
print(f" 时间: {alarm_time} ms")
print(f" 描述: {alarm_desc}")
if __name__ == "__main__":
# 创建ID过滤器,只订阅特定告警ID的告警信息
id_filter = [False] * 128
id_filter[ALARM_RAS_SENTRY_EVENT - 1001] = True # 启用BMC RAS告警事件
client_id = xalarm_register(alarm_handler, id_filter)
if client_id != 0:
print("告警注册失败")
exit(1)
print("告警注册成功,等待接收告警...")
time.sleep(100)
# 程序退出时注销
xalarm_unregister(client_id)C接口
安装相关软件包
libxalarm是sysSentry提供的C语言告警上报和订阅库,用于与xalarmd服务进行通信。使用前需要安装相关软件包:
[root@openEuler ~]# yum install -y libxalarm开发环境还需要安装libxalarm-devel包(构建依赖,非运行依赖):
[root@openEuler ~]# yum install -y libxalarm-devel头文件引用
使用libxalarm接口时,需要包含以下头文件:
#include <xalarm/register_xalarm.h>编译时需要链接libxalarm库:
gcc your_program.c -o your_program -lxalarm数据结构说明
alarm_info结构体
告警信息结构体,用于socket通信格式。
struct alarm_info {
unsigned short usAlarmId; // 告警ID
unsigned char ucAlarmLevel; // 告警级别
unsigned char ucAlarmType; // 告警类别
struct timeval AlarmTime; // 告警生成时间戳
char pucParas[ALARM_INFO_MAX_PARAS_LEN]; // 告警描述信息
};alarm_msg结构体
告警消息结构体,用于事件订阅获取告警信息。
struct alarm_msg {
unsigned short usAlarmId; // 告警ID
struct timeval AlarmTime; // 告警生成时间戳
char pucParas[ALARM_INFO_MAX_PARAS_LEN]; // 告警描述信息
};alarm_subscription_info结构体
告警订阅信息结构体,用于指定订阅的告警ID列表。
struct alarm_subscription_info {
int id_list[MAX_NUM_OF_ALARM_ID]; // 订阅的告警ID列表
unsigned int len; // 列表长度
};告警订阅接口(回调模式)
xalarm_Register
| 接口 | int xalarm_Register(alarm_callback_func callback, struct alarm_subscription_info id_filter); |
|---|---|
| 描述 | 注册告警订阅,通过回调函数接收告警信息 |
| 参数 | callback -- 告警回调函数,类型为void (*)(struct alarm_info *) id_filter -- 告警订阅信息,指定订阅的告警ID列表 |
| 限制 | 1. 回调函数不能为NULL 2. id_filter.len不能超过128 3. id_filter中的每个告警ID必须在1001-1128范围内 4. 不支持多线程使用,不是信号安全函数 5. 同一进程只能注册一次 6.alarm_subscription_info类型入参中len的大小需要与用户填充到id_list中告警ID的个数一致. |
| 返回值 | 返回0表示成功,失败返回-1 |
回调函数类型定义:
typedef void (*alarm_callback_func)(struct alarm_info *palarm);xalarm_UnRegister
| 接口 | void xalarm_UnRegister(int client_id); |
|---|---|
| 描述 | 取消告警订阅注册,停止接收告警信息 |
| 参数 | client_id -- 客户端ID,当前仅支持传入0 |
| 限制 | client_id必须为0 |
| 返回值 | 无 |
xalarm_Upgrade
| 接口 | bool xalarm_Upgrade(struct alarm_subscription_info id_filter, int client_id); |
|---|---|
| 描述 | 更新订阅的告警ID列表 |
| 参数 | id_filter -- 新的告警订阅信息 client_id -- 客户端ID,当前仅支持传入0 |
| 限制 | 1. 必须已经通过xalarm_Register注册 2. client_id必须为0 3. id_filter.len不能超过128 4. id_filter中的每个告警ID必须在1001-1128范围内 |
| 返回值 | 返回true表示成功,失败返回false |
xalarm_getid
| 接口 | unsigned short xalarm_getid(const struct alarm_info *palarm); |
|---|---|
| 描述 | 获取告警ID |
| 参数 | palarm -- 告警信息结构体指针 |
| 返回值 | 返回告警ID,若palarm为NULL则返回0 |
xalarm_getlevel
| 接口 | unsigned char xalarm_getlevel(const struct alarm_info *palarm); |
|---|---|
| 描述 | 获取告警级别 |
| 参数 | palarm -- 告警信息结构体指针 |
| 返回值 | 返回告警级别(MINOR_ALM/MAJOR_ALM/CRITICAL_ALM);若palarm为NULL则返回0,表示获取不到告警 级别 |
xalarm_gettype
| 接口 | unsigned char xalarm_gettype(const struct alarm_info *palarm); |
|---|---|
| 描述 | 获取告警类别 |
| 参数 | palarm -- 告警信息结构体指针 |
| 返回值 | 返回告警类别(ALARM_TYPE_OCCUR/ALARM_TYPE_RECOVER),若palarm为NULL则返回0 |
xalarm_gettime
| 接口 | long long xalarm_gettime(const struct alarm_info *palarm); |
|---|---|
| 描述 | 获取告警时间戳 |
| 参数 | palarm -- 告警信息结构体指针 |
| 返回值 | 返回告警时间戳(毫秒); 返回0则表示时间存在异常 |
xalarm_getdesc
| 接口 | char *xalarm_getdesc(const struct alarm_info *palarm); |
|---|---|
| 描述 | 获取告警描述信息 |
| 参数 | palarm -- 告警信息结构体指针 |
| 返回值 | 返回告警描述信息字符串指针,若palarm为NULL则返回NULL |
使用示例
#include <stdio.h>
#include <xalarm/register_xalarm.h>
void alarm_handler(struct alarm_info *palarm)
{
printf("Received alarm id: %d\n", xalarm_getid(palarm));
printf("Alarm level: %d\n", xalarm_getlevel(palarm));
printf("Alarm message: %s\n", xalarm_getdesc(palarm));
}
int main(int argc, char **argv)
{
struct alarm_subscription_info id_filter;
id_filter.id_list[0] = ALARM_OOM_EVENT;
id_filter.id_list[1] = ALARM_PANIC_EVENT;
id_filter.len = 2;
int client_id = xalarm_Register(alarm_handler, id_filter);
if (client_id != 0) {
printf("register failed.\n");
return -1;
}
// 等待接收告警...
sleep(60);
// 动态更新订阅列表
struct alarm_subscription_info new_filter;
new_filter.id_list[0] = ALARM_OOM_EVENT;
new_filter.id_list[1] = ALARM_KERNEL_REBOOT_EVENT;
new_filter.len = 2;
bool ret = xalarm_Upgrade(new_filter, client_id);
if (!ret) {
printf("upgrade failed.\n");
} else {
// 等待接收新的事件告警...
sleep(60);
}
xalarm_UnRegister(client_id);
return 0;
}日志记录
sysSentry框架运行过程中产生的日志保存在在/var/log/sysSentry/sysSentry.log中,可通过查看该日志获取框架运行详情。
插件日志记录位置和格式
开发巡检插件时,推荐插件运行过程中产生的日志保存在/var/log/sysSentry/目录下,文件名命名为[插件名].log;日志文件名称推荐和插件名称一致。
日志记录相关信息时,推荐日志格式为:<时间戳> - <日志级别> - [<文件名:行号>] - <日志消息> 其中时间戳格式为:YYYY-MM-DD HH:MM:SS,FF , 时间戳示例为:2006-01-02 15:04:05.99
日志级别可选项为:debug/info/warning/error/critical。 选项的含义如下:
- debug:最低级别,用于记录详细的技术信息,帮助开发调试。
- info:记录程序的正常运行信息,如启动和关闭状态。
- warning:记录可能引起问题的情况,但不影响程序运行。
- error:记录严重问题,导致功能失败或程序中断。
- critical:最高级别,记录非常严重的问题,可能导致程序完全停止运行。
推荐程序正常运行时,日志级别设为info。
插件日志轮转配置
sysSentry框架及插件的日志会基于logrotate机制进行自动轮转。日志轮转是一种系统管理技术,用于管理日志文件的大小和数量,以防止日志文件占用过多的磁盘空间。
系统每次触发logrotate时会对sysSentry框架及插件的日志文件大小进行判断,如果日志文件超过4096k,则进行logrotate,一个插件/服务的日志最多轮转两次,轮转日志会被压缩。
插件日志轮转配置示例
日志轮转配置可参考/etc/logrotate.d/sysSentry文件进行配置。 当前/etc/logrotate.d/sysSentry的配置内容如下:
/var/log/sysSentry/*.log{
compress
missingok
notifempty
copytruncate
rotate 2
size +4096k
hourly
}其中各项配置项含义为:
/var/log/sysSentry/*.log表明对哪些日志文件进行转储,*为通配符,表示所有/var/log/sysSentry/目录下以.log结尾的日志文件。- compress表明默认将日志文件进行压缩,节省内存空间。也可配置为nocompress,表明不压缩日志文件。推荐此项默认配置为compress。
- missingok表明如果日志缺失,logrotate不会报错,也不会停止处理其他日志文件。推荐此配置项默认配置。
- notifempty表明如果日志文件为空,则不进行转储。推荐此配置项默认配置。
- copytruncate表明在日志转储时,先将当前的日志文件复制一份,然后将日志文件清空,再将复制的文件进行压缩。这通常用于正在被系统进程使用的日志文件,确保进程可以继续写入新的日志。推荐此配置项默认配置。
- rotate 2表明在轮转操作后保留2个旧的日志文件。一旦旧的文件数量超过2个,logrotate会自动删除旧的日志文件为新的日志文件腾出空间。可根据需求自行定义所需日志数量大小。
- size +4096k表明当日志文件大小超过4096k时,将自动触发日志转储操作。可根据需求自行定义所需日志文件大小。
- hourly表明日志轮转的频率是每小时。可配置项有hourly/daily/weekly/monthly等。示例中表明logrotate每小时轮转日志文件。推荐此项默认配置为hourly。
更改插件日志轮转配置
对新增插件:
可更改
/etc/logrotate.d/sysSentry已有的配置项内容(注:此举会更改所有/var/log/sysSentry/目录下以.log结尾的日志文件配置).若想对
/var/log/sysSentry/目录下不同的日志文件设置不同配置,可在/etc/logrotate.d/sysSentry对每一个日志文件进行单独配置,如下所示:shell/var/log/sysSentry/`[日志名一]`.log{ # 所需配置项等 } /var/log/sysSentry/`[日志名二]`.log{ # 所需配置项等 }
请不要对同一日志,设置不同配置。如下例所示:
# A BAD EXAMPLE
/var/log/sysSentry/*.log{
compress
}
/var/log/sysSentry/example.log{
nocompress
}在该示例中,*为通配符,表示所有/var/log/sysSentry/目录下以.log结尾的日志文件,已经包含了example.log,设置为轮转日志需要压缩;但配置文件中又单独设置example.log不需要压缩。该做法可能会造成预期之外的行为。
如果手动修改了/etc/logrotate.d/sysSentry文件,可通过logrotate -f /etc/logrotate.d/sysSentry手动触发日志轮转。 logrotate的其他使用方法请参考https://linux.die.net/man/8/logrotate
结果上报
用户可通过sysSentry提供的接口将插件巡检结果上报给sysSentry服务,并通过get_result命令查看结果:
[root@openEuler ~]# sentryctl get_result <插件名>提供python与c两种语言的对外接口。
结果上报使用限制
- 结果上报接口应在巡检插件运行结束前被调用。
- 巡检插件的一个生命中期内仅应上报一次巡检结果。
- 巡检插件运行成功或失败均应该上报巡检结果。
python巡检结果上报接口
结构体 ResultLevel
class ResultLevel(Enum):
"""result level for report_result"""
PASS = 0 # 巡检任务正常运行结束,无异常
FAIL = 1 # 因缺少依赖、环境不支持等原因跳过/未执行巡检任务
SKIP = 2 # 因执行命令错误等原因,巡检任务执行失败
MINOR_ALM = 3 # 巡检任务结束,存在系统存在异常,并且已经尝试自动隔离等方式完成修复
MAJOR_ALM = 4 # 巡检任务结束,系统存在异常,需要通过重启等方式修复
CRITICAL_ALM = 5 # 巡检任务结束,致命告警,系统或硬件存在无法修复问题,建议更换接口 结果上报
| 接口 | int report_result(task_name: str, result_level : ResultLevel, report_data : str) -> int |
|---|---|
| 描述 | 用于模块工具向sysSentry上报巡检结果 |
| 参数 | task_name -- 巡检任务名称 result_level -- 巡检结果异常等级,可选参数请参考ResultLevel结构体 report_data -- 巡检结果详细信息,应为json格式转换而成的字符串 |
| 返回值 | 正常:0,异常:非0 |
示例:
[root@openEuler ~]# python3
Python 3.7.9 (default, Dec 11 2023, 19:40:40)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import json
>>> from syssentry.result import ResultLevel, report_result
>>> report_result("test", ResultLevel.PASS, json.dumps({}))
0c巡检结果上报接口
需要安装libxalarm软件包:
[root@openEuler ~]# yum install -y libxalarm开发环境还需要安装libxalarm-devel包(构建依赖,非运行依赖):
[root@openEuler ~]# yum install -y libxalarm-develenum RESULT_LEVEL {
RESULT_LEVEL_PASS = 0, // 巡检任务正常运行结束,无异常
RESULT_LEVEL_FAIL = 1, // 因缺少依赖、环境不支持等原因跳过/未执行巡检任务
RESULT_LEVEL_SKIP = 2, // 因执行命令错误等原因,巡检任务执行失败
RESULT_LEVEL_MINOR_ALM = 3, // 巡检任务结束,存在系统存在异常,并且已经尝试自动隔离等方式完成修复
RESULT_LEVEL_MAJOR_ALM = 4, // 巡检任务结束,系统存在异常,需要通过重启等方式修复
RESULT_LEVEL_CRITICAL_ALM = 5, // 巡检任务结束,致命告警,系统或硬件存在无法修复问题,建议更换
};接口:结果上报
| 接口 | int report_result(const char *task_name, enum RESULT_LEVEL result_level, const char *report_data); |
|---|---|
| 描述 | 用于模块工具向sysSentry上报巡检结果 |
| 参数 | task_name:巡检任务名称 result_level:巡检结果异常等级,可选参数请参考ResultLevel结构体 report_data:巡检结果详细信息,应为json格式转换而成的字符串 |
| 返回值 | 正常:0,异常:非0 |
示例:
[root@openEuler ~]# cat report_res.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <xalarm/register_xalarm.h>
int main(int argc, char *argv[]) {
enum RESULT_LEVEL result_lv = 0;
result_lv = RESULT_LEVEL_PASS;
details = "{\"a\": 1, \"b\": 2}";
int res = report_result(task_name, result_lv, details);
if (res == -1) {
printf("failed send result to sysSentry\n");
}
return 0;
}
[root@openEuler ~]# gcc report_res.c -o report_res -lxalarm对接sentryCollector采集服务
sysSentry软件中包含用来做数据采集的服务:sentryCollector,用户可通过sentryCollector服务定期采集系统数据。
采集使用限制
- 当前仅支持对系统io数据进行采集,并提供ebpf采集和内核无锁采集两种方案;
- 仅支持对nvme ssd(nvme固态硬盘)、sata ssd(sata固态硬盘)、sata hdd(sata机械硬盘)三种磁盘的数据进行采集;
- sentryCollector的io数据采集默认使用ebpf采集,如需使用内核无锁采集,请参考常见问题解决方法 - Q1进行部署。
io数据采集
sentryCollector支持按周期采集指定磁盘的数据,并提供两种采集方式:
- 内核无锁采集 -- 由内核进行数据采集,并将采集结果上报给用户态读取,需要重编内核实现。
- ebpf采集 -- 通过使用ebpf向内核打点的方式进行采集,无需重编内核。
内核无锁采集和ebpf采集的差异
内存无锁采集和ebpf采集在以下几个方面存在区别:
支持采集的阶段不同
将一个io从发生到结束分成多个不同的阶段,以下是两种采集方式支持的阶段类型:
阶段 bio rq_driver throtl wbt gettag plug deadline bpf requeue hctx 内核无锁采集 支持 支持 支持 支持 支持 支持 支持 支持 支持 支持 ebpf采集 支持 支持 不支持 支持 支持 不支持 不支持 不支持 不支持 不支持 支持采集的IO类型不同
IO类型 内核无锁采集 ebpf采集 数据含义 read 支持 支持 读IO write 支持 支持 写IO flush 支持 不支持 flush IO discard 支持 不支持 discard IO
除以上两个差异之外,内核无锁采集和ebpf采集方案可采的数据类型、支持系统等数据均相同:
- 两种采集方案均支持4.19内核,x86_64和aarch64架构。
- 两种采集方案均支持采集nvme ssd(nvme固态硬盘)、sata ssd(sata固态硬盘)和sata hdd(sata机械硬盘)三种盘的数据。
- 两种采集方案均支持对latency、iodump、iolength、iops数据的采集:
- latency -- 指定周期内的时延数据;
- iodump -- 指定周期内超时未完成的io数量,如io运行超过1秒未完成即为超时;
- iolength -- io队列长度
- iops -- 每秒内完成的io数量
对外接口
当前仅提供python语言的接口,需要用户安装pysentry_collect软件包:
[root@openEuler ~]# yum install -y pysentry_collect接口一 查看磁盘类型
| 接口 | get_disk_type(disk) |
|---|---|
| 描述 | 从采集模块中查询磁盘类型 |
| 参数 | disk – 磁盘名,例:sda,必选参数 |
| 限制 | 磁盘名不超过32个字符 |
| 返回值 | 返回值格式为:{"ret": value1, "message":value2} value1取值为0或者其他正整数,0表示成功,其他非零表示失败; message是个字符串,表示表示磁盘类型,字符串类型,如果ret为非零,则message为空字符串,当前支持的message对应磁盘类型如下: "message": "0" -- nvme_ssd "message": "1" -- sata_ssd "message": "2" -- sata_hdd 返回值示例: - 磁盘类型不支持:{"ret": 8, "message": ""} # ret结果非0 - 函数执行成功:{"ret": 0, "message": "1"} # 磁盘为sata ssd类型 |
示例:
[root@openEuler ~]# python3
Python 3.7.9 (default, Dec 11 2023, 19:40:40)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from sentryCollector.collect_plugin import get_disk_type, Disk_Type
>>> res = get_disk_type("sda")
>>> res
{'ret': 0, 'message': '1'}
>>> curr_disk_type = int(res['message'])
>>> curr_disk_type
1
>>> Disk_Type[curr_disk_type]
'sata_ssd'接口二 查询采集是否合法
| 接口 | is_iocollect_valid(period, disk_list=None, stage=None) |
|---|---|
| 描述 | 确认是否在采集范围内,确认周期是否合法 |
| 参数 | period – 用户采集周期,整形,单位秒,必选参数 disk_list – 磁盘列表,默认为None,代表关注所有磁盘,可选参数。可传入自定义列表,例:["sda", "sdb", "sdv"] stage – 采集阶段,默认为None,代表关注所有采集阶段,可选参数。可传入自定义阶段列表,例:["wbt", "bio"] |
| 限制 | 1. 采集周期取值在1到300之间 2. 磁盘列表磁盘个数不超过10个,如果超过10个,默认取前10个,磁盘列表种的磁盘名不超过32个字符 3. 采集阶段个数不超过15个,阶段名字符不超过20个字符 |
| 返回值 | 返回值格式为:{"ret": value1, "message":value2} value1取值为0或者其他正整数,0表示成功,其他非零表示失败 message是个字符串,表示有效的磁盘和该磁盘对应的stage,字符串类型,如果字符串为空说明全都不支持,格式如下: {"disk_name1": ["stage1", "stage2"], "disk_name2": ["stage1", "stage2"], ...} 返回值示例: - 验证失败:{"ret": 1, "message": {}} # ret结果非0 - 验证成功,所有盘均不支持采集:{"ret": 0, "message": {}} - 部分盘不支持(message中返回支持的盘和对应的阶段):{"ret": 0, "message": {"sda": ["bio", "gettag"], "sdb": ["bio", "gettag"]}} |
示例:
[root@openEuler ~]# python3
Python 3.7.9 (default, Dec 11 2023, 19:40:40)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from sentryCollector.collect_plugin import is_iocollect_valid
>>> is_iocollect_valid(1, ["sda"])
{'ret': 0, 'message': '{"sda": ["throtl", "wbt", "gettag", "plug", "deadline", "hctx", "requeue", "rq_driver", "bio"]}'}接口三 查询指定数据
| 接口 | get_io_data(period, disk_list, stage, iotype) |
|---|---|
| 功能 | 确认是否在采集范围内,确认周期是否合法 |
| 参数 | period – 用户采集周期,整形,单位秒,必选参数 disk_list -- 磁盘列表,必选参数,例:["sda", "sdb", "sdv"] stage – 读取的阶段,必选参数,例:["bio", "gettag", "wbt"]。 iotype – IO类型,列表中对应要获取的IO数据类型,仅支持read/write/flush/discard,必选参数,例:["read", "write"] |
| 限制 | 1. 采集周期取值在1到300之间,并且为period_time值的整数倍,且倍数不超过max_save(两个数值请参考/etc/sysSentry/sentryCollector.conf) 2. 磁盘列表磁盘个数不超过10个,如果超过10个,默认取前10个,磁盘列表种的磁盘名不超过32个字符 3. 采集阶段个数不超过15个,阶段名字符不超过20个字符 4. IO类型个数不超过4个,字符长度不超过7个(最长的长度是discard) |
| 返回值 | 返回值格式为:{"ret": value1, "message":value2} value1取值为0或者其他正整数,0表示成功,其他非零表示失败 message是个字符串,表示采集模块处理过的当前周期数据,字符串类型,格式如下: "{"disk_name1": {"stage1": {"read": [latency, iodump, iolength, iops],"write": [write_latency, write_iodump, iolength, iops]},"stage2": {…}},…}" 返回值示例: - 获取数据失败:{"ret": 1, "message": {}} # ret结果非0 - 获取数据成功:{"ret": 0, "message": "{"sda": {"bio": {"read": [0.1, 0, 100, 19], "write": [0.5, 3, 100, 12]}, "wbt": {}}, "sdb"…}"} 其中[0.5, 3, 100, 12]对应[时延ns,iodumps数量,io队列长度,iops] |
示例:
[root@openEuler ~]# python3
Python 3.7.9 (default, Dec 11 2023, 19:40:40)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from sentryCollector.collect_plugin import get_io_data
>>> get_io_data(1, ["sda"], ["bio"], ["read"])
{'ret': 0, 'message': '{"sda": {"bio": {"read": [0, 0, 0, 0]}}}'}