Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

飞书告警通知 #53

Open
FrankDong945 opened this issue Jul 26, 2021 · 0 comments
Open

飞书告警通知 #53

FrankDong945 opened this issue Jul 26, 2021 · 0 comments

Comments

@FrankDong945
Copy link

FrankDong945 commented Jul 26, 2021

升级python

1.安装编译环境包(防止出现安装错误)
yum install gcc-c++ gcc make cmake zlib-devel bzip2-devel openssl-devel ncurse-devel libffi-devel -y
2.在线下载Python3.7源码包
#进入tmp目录
cd /tmp
#下载python3.7.3
wget https://www.python.org/ftp/python/3.7.3/Python-3.7.3.tar.xz
3.解压并配置

#解压
tar Jxvf Python-3.7.3.tar.xz
#进入python3.7.3目录
cd Python-3.7.3
#创建目录
mkdir -p /usr/local/python3
#配置(指定安装目录)
./configure --prefix=/usr/local/python3 --enable-optimizations
4.编译及安装

make && make install
5.更换系统默认Python版本
1).备份原系统旧版本python

mv /usr/bin/python /usr/bin/python.bak
mkdir /usr/bin/pip
mv /usr/bin/pip /usr/bin/pip.bak
2).配置环境变量:创建新版本Python和pip的软链接

ln -s /usr/local/python3/bin/python3.7 /usr/bin/python
ln -s /usr/local/python3/bin/pip3 /usr/bin/pip
3).查看python版本

python -V
6.修改yum功能
因为yum的功能依赖Pyhon2,现在更改默认Python版本后会导致yum无法正常工作,所以进行以下3处修复
第1处:
vim /usr/bin/yum

改成:#! /usr/bin/python2.7

vim /usr/libexec/urlgrabber-ext-down

把最顶部的
改成:#! /usr/bin/python2.7

vim /usr/sbin/firewalld
vim /usr/bin/firewall-cmd

修改N9E server.yaml文件
vim /opt/n9e/server/etc/server.yml
contactKeys:

  • label: "Feishu Robot Token" ##新增加
    key: feishu_robot_token ##新增加

notifyChannels:

  • feishu ##新增加

修改N9E告警脚本
vim /opt/n9e/server/etc/script/notify.py
替换为以下内容

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys
import json
import os
import smtplib
import time
import requests
from email.mime.text import MIMEText
from email.header import Header

# 希望的demo实现效果:
# 1. 从stdin拿到告警信息之后,格式化为一个有缩进的json写入一个临时文件
# 2. 文件路径和名字是.alerts/${timestamp}_${ruleid}
# 3. 调用SMTP服务器发送告警,微信、钉钉、飞书、slack、jira、短信、电话等等留给社区实现

# 脚本二开指南
# 1. 可以根据下面的TEST_ALERT_JSON 中的结构修改脚本发送逻辑,定制化告警格式格式如下
"""
[告警类型:prometheus]
[规则名称:a]
[是否已恢复:已触发]
[告警级别:1]
[触发时间:2021-07-02 16:05:14]
[可读表达式:go_goroutines>0]
[当前值:[vector={__name__="go_goroutines", instance="localhost:9090", job="prometheus"}]: [value=33.000000]]
[标签组:instance=localhost:9090 job=prometheus]
"""
# 2. 每个告警会以json文件的格式存储在LOCAL_EVENT_FILE_DIR 下面,文件名为 filename = '%d_%d_%d' % (rule_id, event_id, trigger_time)
# 3. 告警通道需要自行定义Send类中的send_xxx同名方法,反射调用:举例 event.notify_channels = [qq dingding] 则需要Send类中 有 send_qq send_dingding方法
# 4. im发群信息,比如钉钉发群信息需要群的webhook机器人 token,这个信息可以在user的contacts map中,各个send_方法处理即可
# 5. 用户创建一个虚拟的用户保存上述im群 的机器人token信息 user的contacts map中

mail_host = "smtp.qq.com"
mail_port = 994
mail_user = "ulricqin"
mail_pass = "password"
mail_from = "751958224@qq.com"

# 本地告警event json存储目录
LOCAL_EVENT_FILE_DIR = ".alerts"
NOTIFY_CHANNELS_SPLIT_STR = " "

# 群机器人token 配置字段
FEISHU_ROBOT_TOKEN_NAME = "feishu_robot_token"
FEISHU_API = "修改为机器人webhook 地址"

# stdin 告警json实例
TEST_ALERT_JSON = {
    "event": {
        "alert_duration": 10,
        "notify_channels": "feishu",
        "res_classpaths": "",
        "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": "",
        "rule_name": "a",
        "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": 0,
        "rule_id": 1
    },
    "rule": {
        "alert_duration": 10,
        "notify_channels": "feishu",
        "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": {
                "feishu_robot_token": "xxxxxxx"
            },
            "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 = content_gen(payload)

    for ch in notify_channels:
        send_func_name = "send_{}".format(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 content_gen(payload):
    # 生成格式化告警内容
    text = ""
    event_obj = payload.get("event")

    rule_type = event_obj.get("is_prome_pull")
    type_str_m = {1: "prometheus", 0: "n9e"}
    rule_type = type_str_m.get(rule_type)

    text += "[告警类型:{}]\n".format(rule_type)

    rule_name = event_obj.get("rule_name")
    text += "[规则名称:{}]\n".format(rule_name)

    is_recovery = event_obj.get("is_recovery")
    is_recovery_str_m = {1: "已恢复", 0: "已触发"}
    is_recovery = is_recovery_str_m.get(is_recovery)
    text += "[是否已恢复:{}]\n".format(is_recovery)

    priority = event_obj.get("priority")
    text += "[告警级别:{}]\n".format(priority)

    trigger_time = event_obj.get("trigger_time")
    text += "[触发时间:{}]\n".format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(trigger_time))))

    readable_expression = event_obj.get("readable_expression")
    text += "[可读表达式:{}]\n".format(readable_expression)

    values = event_obj.get("values")
    text += "[当前值:{}]\n".format(values)

    tags = event_obj.get("tags")
    text += "[标签组:{}]\n".format(tags)

    print(text)
    return text


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_feishu(cls, alert_content, payload):
        # 飞书发群信息需要群的webhook机器人 token,这个信息可以在user的contacts map中

        users = payload.get("users")

        for u in users:
            contacts = u.get("contacts")

            feishu_robot_token = contacts.get(FEISHU_ROBOT_TOKEN_NAME, "")

            if feishu_robot_token == "":
                print("feishu_robot_token_not_found")
                continue

            feishu_api_url = "修改为机器人webhook地址"
            atMobiles = [u.get("phone")]
            headers = {'Content-Type': 'application/json;charset=utf-8'}
            pay_load = {
                "msg_type": "text",
                "content": {
                    "text": alert_content
                },
                "at": {
                    "atMobiles": atMobiles,
                    "isAtAll": False
                }
            }
            res = requests.post(feishu_api_url, json.dumps(pay_load), headers=headers)
            print(res.status_code)
            print(res.text)

            print("send_feishu")


def mail_test():
    print("mail_test_todo")

    recipients = ["ulricqin@qq.com", "ulric@163.com"]

    message = MIMEText(mail_body, 'html', 'utf-8')
    message['From'] = mail_from
    message['To'] = ", ".join(recipients)
    message["Subject"] = "n9e alert"

    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")
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant