0x01 前言
我的zabbix一直使用邮件进行告警,可是邮件告警有将近5分钟的延迟,主要是我的邮箱不支持推送。因此我编写一个简单的python脚本处理zabbix告警信息并转发到其他脚本。
其他脚本可以是调用微信API的脚本,可以是短信平台的API等。但在调用之前需要先处理zabbix发送过来的内容,因为原始内容中很大的一部分是固定的,可以将其替换为中文等。
0x02 准备
首先需要准备zabbix环境,如果你没有做任何修改,那么告警信息是这样的:
Trigger: ngx.hk \u975e200\u54cd\u5e94 Trigger status: OK Trigger severity: High Trigger URL: https://ngx.hk Item values: 1. Response code for step "ngx.hk" of scenario "ngx.hk". (Zabbix server:web.test.rspcode[ngx.hk,ngx.hk]): 200 2. *UNKNOWN* (*UNKNOWN*:*UNKNOWN*): *UNKNOWN* 3. *UNKNOWN* (*UNKNOWN*:*UNKNOWN*): *UNKNOWN* Original event ID: 3793899
然后需要准备python 3环境。
0x03 脚本
0x03.1 内容替换
根据上面的日志,我们可以将一些内容替换为中文,以下是相关的字典:
# 需要替换的内容 msg_description_mapping_dict = {'Trigger': '触发器', 'status': '状态', 'severity': '告警等级', 'URL': 'URL', 'Item values': '触发项目', 'Original event ID': '事件ID', 'Not classified': '未归类', 'Information': '通知', 'Warning': '警告', 'Average': '中等', 'High': '严重', 'Disaster': '灾难', 'PROBLEM': '异常', 'OK': '正常' }
有了字典,我选择用遍历字典的方式替换原始内容:
# 遍历字典,替换内容 for key, values in msg_description_mapping_dict.items(): msg = msg.replace(key, values)
运行后的内容如下:
MacBook-Air-wifi:~ terence$ python3 /Users/terence/Documents/git_home/zabbix_msg/t1.py ' > Trigger: ngx.hk \u975e200\u54cd\u5e94 > Trigger status: OK > Trigger severity: High > Trigger URL: https://ngx.hk > > Item values: > > 1. Response code for step "ngx.hk" of scenario "ngx.hk". (Zabbix server:web.test.rspcode[ngx.hk,ngx.hk]): 200 > 2. *UNKNOWN* (*UNKNOWN*:*UNKNOWN*): *UNKNOWN* > 3. *UNKNOWN* (*UNKNOWN*:*UNKNOWN*): *UNKNOWN* > > Original event ID: 3793899' 触发器: ngx.hk \u975e200\u54cd\u5e94 触发器 状态: 正常 触发器 告警等级: 严重 触发器 URL: https://ngx.hk 触发项目: 1. Response code for step "ngx.hk" of scenario "ngx.hk". (Zabbix server:web.test.rspcode[ngx.hk,ngx.hk]): 200 2. *UNKNOWN* (*UNKNOWN*:*UNKNOWN*): *UNKNOWN* 3. *UNKNOWN* (*UNKNOWN*:*UNKNOWN*): *UNKNOWN* 事件ID: 3793899
0x03.2 unicode
我在建立自定义zabbix告警Trigger时使用了中文,例如上面日志中的:
Trigger: ngx.hk \u975e200\u54cd\u5e94
将其中unicode编码文字解码后的意思是:
非200响应
这个Trigger是监控我的网站是在线,同时为了方便理解,所以我用中文作为Trigger名。
所以我使用正则将所有unicode编码的字符匹配并编组:
# 匹配unicode的正则 re_pattern = re.compile(r'\\u\w*') # 找到所有unicode字符并编组 unicode_msg = re_pattern.findall(msg)
这里有个问题,zabbix在将内容发送到自定义脚本前会将字符编码为unicode,这时候python接收到的内容全为字符串,如上面日志所示。需要先将字符串编码再解码,因为字符串不能直接解码:
# 匹配unicode的正则 re_pattern = re.compile(r'\\u\w*') # 找到所有unicode字符并编组 unicode_msg = re_pattern.findall(msg) # 将unicode编码为中文并替换掉msg里的unicode for i in unicode_msg: j = i[0:] j = j.encode('latin-1').decode('unicode_escape') msg = msg.replace(i, j)
运行后的结果如下:
触发器: ngx.hk 非200响应
0x03.3 shell
像这种小脚本,而且参数格式比较单一的脚本,我喜欢用字典传递参数。同时我调用了企业微信的API,所以相关的字典内容如下:
# dict script_parameter = dict() script_parameter['corpid'] = corpid script_parameter['corpsecret'] = corpsecret script_parameter['msg'] = msg script_parameter['app_id'] = app_id script_parameter['group_id'] = group_id
调用微信API时会有回调内容,在这里我使用subprocess进行shell调用,首先要定义命令:
# 定义shell命令 cmd = ['python3', script_path, str(script_parameter)]
然后定义调用subprocess的方法:
# 定义调用shell命令的方法 send_msg = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
最后是实施:
# 调用shell命令 send_msg_callback, send_msg_err = send_msg.communicate()
因为时间关系,在这里我并没有写回调内容,但在日后会更新。
完整的脚本如下:
#!/usr/bin/python3 # -*- coding=utf-8 -*- import re import subprocess import sys # 接收传入内容 msg = sys.argv[1] # 企业ID corpid = 'your corpid' # 应用的凭证密钥 corpsecret = 'your corpsecret here' # 企业应用的id app_id = 1 # 部门ID group_id = 1 # 脚本的相对路径 script_path = '/usr/local/shell/wechat_msg/main.py' # 需要替换的内容 msg_description_mapping_dict = {'Trigger': '触发器', 'status': '状态', 'severity': '告警等级', 'URL': 'URL', 'Item values': '触发项目', 'Original event ID': '事件ID', 'Not classified': '未归类', 'Information': '通知', 'Warning': '警告', 'Average': '中等', 'High': '严重', 'Disaster': '灾难', 'PROBLEM': '异常', 'OK': '正常' } # 遍历字典,替换内容 for key, values in msg_description_mapping_dict.items(): msg = msg.replace(key, values) # 匹配unicode的正则 re_pattern = re.compile(r'\\u\w*') # 找到所有unicode字符并编组 unicode_msg = re_pattern.findall(msg) # 将unicode编码为中文并替换掉msg里的unicode for i in unicode_msg: j = i[0:] j = j.encode('latin-1').decode('unicode_escape') msg = msg.replace(i, j) # dict script_parameter = dict() script_parameter['corpid'] = corpid script_parameter['corpsecret'] = corpsecret script_parameter['msg'] = msg script_parameter['app_id'] = app_id script_parameter['group_id'] = group_id # 定义shell命令 cmd = ['python3', script_path, str(script_parameter)] # 定义调用shell命令的方法 send_msg = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE) # 调用shell命令 send_msg_callback, send_msg_err = send_msg.communicate()
0x04 zabbix
完成脚本后需要将脚本上传到zabbix的服务器中,我的路径如下:
[root@web ~]# ll /usr/local/shell/zabbix/ 总用量 4 -rwx--x--x 1 zabbix zabbix 2142 9月 30 18:04 main.py
需要将脚本赋予可执行权限并将用户修改为zabbix,然后在zabbix控制面板中添加一个Media types:
主要内容如下:
完成Media types的添加后还需要添加用户的告警media:
在控制面板中修改完成后还需要修改zabbix server的配置文件,指定脚本的路径:
#打开文件并编辑 [root@web ~]# vim /usr/local/zabbix/etc/zabbix_server.conf #找到以下内容 AlertScriptsPath=${datadir}/zabbix/alertscripts #去掉注释符号并修改为 AlertScriptsPath=/usr/local/shell/zabbix
然后重新启动zabbix server即可,因为我的zabbix server是自行编译且没配置管理器,所以只能killall了:
#kill掉所有的服务 [root@web ~]# killall zabbix_server #启动服务 [root@web ~]# zabbix_server
0x05 结语
因为可能会有网络故障等意外情况发生,如果需要用在生产环境,建议编写回调与记录相关日志的语句。
如果需要测试也很简单,只需要自定义一个告警项即可。