File size: 8,395 Bytes
08c964e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# coding: utf-8
# +-------------------------------------------------------------------
# | 宝塔Windows面板
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2020 宝塔软件(https://www.bt.cn) All rights reserved.
# +-------------------------------------------------------------------
# | Author: baozi <1191604998@qq.com>
# +-------------------------------------------------------------------

import sys
import os
import re
import json
import time
import datetime
import psutil
import threading

class_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# /www/server/panel/class
bt_panel_path = os.path.dirname(class_path)
sys.path.insert(0, class_path)
sys.path.insert(0, bt_panel_path)
import public
import panelPush
from push.base_push import base_push, WxAccountMsg
from panelMysql import panelMysql


class quota_push(base_push):
    __push_conf = os.path.join(public.get_panel_path(), "class/push/push.json")

    def __init__(self):
        self.__push = panelPush.panelPush()


    def get_version_info(self, get=None):
        data = {
            'ps': '宝塔系统告警',
            'version': '1.0',
            'date': '2023-07-16',
            'author': '宝塔',
            'help': 'http://www.bt.cn/bbs'
        }
        return data

    # 格式化返回执行周期, 目前无作用
    def get_push_cycle(self, data: dict):
        return data

    # 获取模块推送参数
    def get_module_config(self, get: public.dict_obj):
        data = []
        item = self.__push.format_push_data(
            push=["mail", 'dingding', 'weixin', "feishu", "wx_account"],
            project='system', type='')
        item['cycle'] = 30
        item['title'] = '磁盘容量限额'
        data.append(item)
        return data

    # 获取模块配置项
    def get_push_config(self, get: public.dict_obj):
        task_id = get.id
        push_list = self.__push._get_conf()

        if task_id not in push_list["quota_push"]:
            res_data = public.returnMsg(False, '未找到指定配置.')
            res_data['code'] = 100
            return res_data
        result = push_list["quota_push"][task_id]
        return result

    @staticmethod
    def clear_push_count(task_id):
        """清除推送次数"""
        task_data_cache.set("tip_" + task_id, [])
        task_data_cache.save_cache()

    # 写入推送配置文件
    def set_push_config(self, get: public.dict_obj):
        return self.__push._get_conf()

    # 删除推送配置
    def del_push_config(self, get: public.dict_obj):
        # 从配置中删除信息,并做一些您想做的事,如记日志
        task_id = get.id
        data = self.__push._get_conf()
        if str(task_id).strip() in data["quota_push"]:
            del data["quota_push"][task_id]
        public.writeFile(self.__push_conf, json.dumps(data))
        return public.returnMsg(True, '删除成功.')

    def get_total(self):
        return True

    def can_view_task(self, data) -> bool:
        return False

    # 检查并获取推送消息,返回空时,不做推送, 传入的data是配置项
    def get_push_data(self, data, total):
        # data 内容
        # index :  时间戳 time.time()
        # 消息 以类型为key, 以内容为value, 内容中包含title 和msg
        # push_keys: 列表,发送了信息的推送任务的id,用来验证推送任务次数() 意义不大
        tip_list = task_data_cache.get("tip_{}".format(data["id"]))
        if tip_list is None:
            tip_list = []
        today = datetime.date.today()
        try:
            for i in range(len(tip_list)-1, -1, -1):
                tip_time = datetime.datetime.fromtimestamp(float(tip_list[i]))
                if tip_time.date() < today:
                    del tip_list[i]
        except ValueError:
            tip_list = []

        if 0 < data["push_count"] <= len(tip_list):
            return None

        result = {'index': datetime.datetime.now().timestamp(), 'push_keys': []}
        try:
            import PluginLoader
            quota_info = PluginLoader.module_run('quota', 'quota_check', int(data["id"]))
        except:
            quota_info = None

        type_msg_dict = {
            "database": "数据库",
            "site": "网站目录",
            "ftp": "FTP 目录",
        }

        if quota_info is not None and quota_info.get("status") is None:
            for m_module in data['module'].split(','):
                type_msg = type_msg_dict.get(data["type"])
                if m_module == 'wx_account':
                    result[m_module] = ToWechatAccountMsg.quota(type_msg, quota_info["db_name"], quota_info["used"])
                else:
                    quota_path = quota_info["path"]
                    if quota_info["quota_type"] == "database":
                        quota_path = quota_info["db_name"]
                    s_list = [
                        ">通知类型:{}容量限额告警".format(type_msg),
                        ">告警内容:{} {} 磁盘使用量 {}MB 已超过 {}MB".format(type_msg, quota_path, round(quota_info["used"] / 1024 / 1024, 2), quota_info["quota_push"]["size"])
                    ]
                    sdata = public.get_push_info("磁盘容量占用告警", s_list)
                    result[m_module] = sdata

            tip_list.append(result["index"])
            task_data_cache.set("tip_{}".format(data["id"]), tip_list)
        return result

    # 返回到前端信息的钩子, 默认为返回传入信息(即:当前设置的任务的信息)
    @staticmethod
    def get_view_msg(task_id, task_data):
        task_data["tid"] = view_msg.get_tid(task_data)
        task_data["view_msg"] = view_msg.get_msg_by_type(task_data)
        return task_data

    @staticmethod
    def _get_bak_task_template():
        return []


class TaskDataCache(object):
    """记录告警检测的平均数据"""
    _FILE = "{}/data/push/tips/quota_data.json".format(public.get_panel_path())

    def __init__(self):
        if not os.path.exists(self._FILE):
            self._data = {}
            if not os.path.exists(os.path.dirname(self._FILE)):
                os.makedirs(os.path.dirname(self._FILE), 0o600)
            self._file_fp = open(self._FILE, mode='w+', encoding='utf-8')
        else:
            try:
                self._file_fp = open(self._FILE, mode='r+', encoding='utf-8')
                self._data = json.load(self._file_fp)
                if not isinstance(self._data, dict):
                    self._data = {}
            except (json.JSONDecodeError, ValueError):
                self._data = {}
                self._file_fp = open(self._FILE, mode='w+', encoding='utf-8')

    def __del__(self):
        self.save_cache()
        self._file_fp.close()

    def save_cache(self):
        self._file_fp.seek(0, 0)
        self._file_fp.truncate()
        json.dump(self._data, self._file_fp)
        self._file_fp.flush()

    def get(self, key):
        return self._data.get(key, None)

    def set(self, key, value):
        self._data[key] = value


task_data_cache = TaskDataCache()


class ViewMsgFormat(object):
    _FORMAT = {
        "database": (
            lambda x: "<span>MySQL 数据库 {} 磁盘占用超过 {}MB 触发</span>".format(x.get("db_name"),x.get("size"))
        ),
        "site": (
            lambda x: "<span>网站目录 {} 磁盘占用超过 {}MB 触发</span>".format(x.get("path"), x.get("size"))
        ),
        "ftp": (
            lambda x: "<span>ftp目录 {} 磁盘占用超过 {}MB 触发</span>".format(x.get("path"), x.get("size"))
        ),
    }

    _TID = {
        "database": "quota_push@0",
        "site": "quota_push@1",
        "ftp": "quota_push@2",
    }

    def get_msg_by_type(self, data):
        return self._FORMAT[data["type"]](data)

    def get_tid(self, data):
        return self._TID[data["type"]]


view_msg = ViewMsgFormat()


class ToWechatAccountMsg:
    @staticmethod
    def quota(type_msg: str, db_name: str, size: int):
        msg = WxAccountMsg.new_msg()
        msg.thing_type = "{} 容量限额告警".format(type_msg)
        msg.msg = "{} {} 磁盘占用超过 {}MB".format(type_msg, db_name, str(size))
        msg.next_msg = "请登录面板,查看主机情况"
        return msg