nightingale/etc/script/notify.py

328 lines
10 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/python
# -*- coding: UTF-8 -*-
#
# n9e-server把告警事件通过stdin的方式传入notify.pynotify.py从事件中解析出接收人信息、拼出通知内容发送通知
# 脚本的灵活性高要接入短信、电话、jira、飞书等都非常容易只要有接口notify.py去调用即可
#
import sys
import json
import os
import smtplib
import time
import requests
from email.mime.text import MIMEText
from email.header import Header
from bottle import template
reload(sys) # reload 才能调用 setdefaultencoding 方法
sys.setdefaultencoding('utf-8') # 设置 'utf-8'
################################
## 邮件告警,修改下面的配置 ##
################################
mail_host = "smtp.163.com"
mail_port = 994
mail_user = "ulricqin"
mail_pass = "password"
mail_from = "ulricqin@163.com"
# 本地告警event json存储目录
LOCAL_EVENT_FILE_DIR = ".alerts"
NOTIFY_CHANNELS_SPLIT_STR = " "
NOTIFY_CHANNEL_DICT = {
"email":"email",
"sms":"sms",
"voice":"voice",
"dingtalk":"dingtalk",
"wecom":"wecom"
}
# stdin 告警json实例
TEST_ALERT_JSON = {
"event": {
"alert_duration": 10,
"notify_channels": "dingtalk",
"res_classpaths": "all",
"id": 4,
"notify_group_objs": None,
"rule_note": "",
"history_points": [
{
"metric": "go_goroutines",
"points": [
{
"t": 1625213114,
"v": 33.0
}
],
"tags": {
"instance": "localhost:9090",
"job": "prometheus"
}
}
],
"priority": 1,
"last_sent": True,
"tag_map": {
"instance": "localhost:9090",
"job": "prometheus"
},
"hash_id": "ecb258d2ca03454ee390a352913c461b",
"status": 0,
"tags": "instance=localhost:9090 job=prometheus",
"trigger_time": 1625213114,
"res_ident": "ident1",
"rule_name": "alert_test",
"is_prome_pull": 1,
"notify_users": "1",
"notify_groups": "",
"runbook_url": "",
"values": "[vector={__name__=\"go_goroutines\", instance=\"localhost:9090\", job=\"prometheus\"}]: [value=33.000000]",
"readable_expression": "go_goroutines>0",
"notify_user_objs": None,
"is_recovery": 1,
"rule_id": 1
},
"rule": {
"alert_duration": 10,
"notify_channels": "dingtalk",
"enable_stime": "00:00",
"id": 1,
"note": "",
"create_by": "root",
"append_tags": "",
"priority": 1,
"update_by": "root",
"type": 1,
"status": 0,
"recovery_notify": 0,
"enable_days_of_week": "1 2 3 4 5 6 7",
"callbacks": "localhost:10000",
"notify_users": "1",
"notify_groups": "",
"runbook_url": "",
"name": "a",
"update_at": 1625211576,
"create_at": 1625211576,
"enable_etime": "23:59",
"group_id": 1,
"expression": {
"evaluation_interval": 4,
"promql": "go_goroutines>0"
}
},
"users": [
{
"username": "root",
"status": 0,
"contacts": {
"dingtalk_robot_token": "xxxxxx"
},
"create_by": "system",
"update_at": 1625211432,
"create_at": 1624871926,
"email": "",
"phone": "",
"role": "Admin",
"update_by": "root",
"portrait": "",
"nickname": "\u8d85\u7ba1",
"id": 1
}
]
}
def main():
payload = json.load(sys.stdin)
trigger_time = payload['event']['trigger_time']
event_id = payload['event']['id']
rule_id = payload['rule']['id']
notify_channels = payload['event'].get('notify_channels').strip().split(NOTIFY_CHANNELS_SPLIT_STR)
if len(notify_channels) == 0:
msg = "notify_channels_empty"
print(msg)
return
# 持久化到本地json文件
persist(payload, rule_id, event_id, trigger_time)
# 生成告警内容
alert_content = sms_content_gen(values_gen(payload))
for ch in notify_channels:
send_func_name = "send_{}".format(NOTIFY_CHANNEL_DICT.get(ch.strip()))
has_func = hasattr(Send, send_func_name)
if not has_func:
msg = "[send_func_name_err][func_not_found_in_Send_class:{}]".format(send_func_name)
print(msg)
continue
send_func = getattr(Send, send_func_name)
send_func(alert_content, payload)
def values_gen(payload):
event_obj = payload.get("event")
values = {
"IsAlert": event_obj.get("is_recovery") == 0,
"IsMachineDep": event_obj.get("res_classpaths") != "",
"Status": status_gen(event_obj.get("priority"),event_obj.get("is_recovery")),
"Sname": event_obj.get("rule_name"),
"Ident": event_obj.get("res_ident"),
"Classpath": event_obj.get("res_classpaths"),
"Metric": metric_gen(event_obj.get("history_points")),
"Tags": event_obj.get("tags"),
"Value": event_obj.get("values"),
"ReadableExpression": event_obj.get("readable_expression"),
"TriggerTime": time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(event_obj.get("trigger_time"))),
"Elink": "http://n9e.didiyun.com/strategy/edit/{}".format(event_obj.get("rule_id")),
"Slink": "http://n9e.didiyun.com/event/{}".format(event_obj.get("id"))
}
return values
def email_content_gen(values):
return template('etc/script/tpl/mail.tpl', values)
def sms_content_gen(values):
return template('etc/script/tpl/sms.tpl', values)
def status_gen(priority,is_recovery):
is_recovery_str_m = {1: "恢复", 0: "告警"}
status = "P{} {}".format(priority, is_recovery_str_m.get(is_recovery))
return status
def subject_gen(priority,is_recovery,rule_name):
is_recovery_str_m = {1: "恢复", 0: "告警"}
subject = "P{} {} {}".format(priority, is_recovery_str_m.get(is_recovery), rule_name)
return subject
def metric_gen(history_points):
metrics = []
for item in history_points:
metrics.append(item.get("metric"))
return ",".join(metrics)
def persist(payload, rule_id, event_id, trigger_time):
if not os.path.exists(LOCAL_EVENT_FILE_DIR):
os.makedirs(LOCAL_EVENT_FILE_DIR)
filename = '%d_%d_%d' % (rule_id, event_id, trigger_time)
filepath = os.path.join(LOCAL_EVENT_FILE_DIR, filename)
with open(filepath, 'w') as f:
f.write(json.dumps(payload, indent=4))
class Send(object):
@classmethod
def send_email(cls, alert_content, payload):
users = payload.get("users")
emails = [x.get("email") for x in users]
if not emails:
return
recipients = emails
mail_body = email_content_gen(values_gen(payload))
message = MIMEText(mail_body, 'html', 'utf-8')
message['From'] = mail_from
message['To'] = ", ".join(recipients)
message["Subject"] = subject_gen(payload.get("event").get("priority"),payload.get("event").get("is_recovery"),payload.get("event").get("rule_name"))
smtp = smtplib.SMTP_SSL(mail_host, mail_port)
smtp.login(mail_user, mail_pass)
smtp.sendmail(mail_from, recipients, message.as_string())
smtp.close()
print("send_mail_success")
@classmethod
def send_wecom(cls, alert_content, payload):
users = payload.get("users")
for u in users:
contacts = u.get("contacts")
wecom_robot_token = contacts.get("wecom_robot_token", "")
if wecom_robot_token == "":
continue
wecom_api_url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={}".format(wecom_robot_token)
atMobiles = [u.get("phone")]
headers = {'Content-Type': 'application/json;charset=utf-8'}
payload = {
"msgtype": "text",
"text": {
"content": alert_content
},
"at": {
"atMobiles": atMobiles,
"isAtAll": False
}
}
res = requests.post(wecom_api_url, json.dumps(payload), headers=headers)
print(res.status_code)
print(res.text)
print("send_wecom")
@classmethod
def send_dingtalk(cls, alert_content, payload):
# 钉钉发群信息需要群的webhook机器人 token这个信息可以在user的contacts map中
users = payload.get("users")
for u in users:
contacts = u.get("contacts")
dingtalk_robot_token = contacts.get("dingtalk_robot_token", "")
if dingtalk_robot_token == "":
print("dingtalk_robot_token_not_found")
continue
dingtalk_api_url = "https://oapi.dingtalk.com/robot/send?access_token={}".format(dingtalk_robot_token)
atMobiles = [u.get("phone")]
headers = {'Content-Type': 'application/json;charset=utf-8'}
payload = {
"msgtype": "text",
"text": {
"content": alert_content
},
"at": {
"atMobiles": atMobiles,
"isAtAll": False
}
}
res = requests.post(dingtalk_api_url, json.dumps(payload), headers=headers)
print(res.status_code)
print(res.text)
print("send_dingtalk")
def mail_test():
print("mail_test_todo")
recipients = ["ulricqin@qq.com", "ulric@163.com"]
payload = json.loads(json.dumps(TEST_ALERT_JSON))
mail_body = email_content_gen(values_gen(payload))
message = MIMEText(mail_body, 'html', 'utf-8')
message['From'] = mail_from
message['To'] = ", ".join(recipients)
message["Subject"] = subject_gen(payload.get("event").get("priority"),payload.get("event").get("is_recovery"),payload.get("event").get("rule_name"))
smtp = smtplib.SMTP_SSL(mail_host, mail_port)
smtp.login(mail_user, mail_pass)
smtp.sendmail(mail_from, recipients, message.as_string())
smtp.close()
print("mail_test_done")
if __name__ == "__main__":
if len(sys.argv) == 1:
main()
elif sys.argv[1] == "mail":
mail_test()
else:
print("I am confused")