├── .idea
├── dingding.iml
├── encodings.xml
├── misc.xml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── README.md
├── conf
├── BLog.py
├── BLog.pyc
├── INIFILES.py
├── INIFILES.pyc
├── __init__.py
├── __init__.pyc
└── file_util.py
├── config.ini
├── dingding.py
├── log
└── 2017-01-06.log
├── requests
├── requests-2.12.4-py2.py3-none-any.whl
└── requests-2.12.4.tar.gz
├── 恢复.jpg
└── 报警.jpg
/.idea/dingding.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
31 |
32 |
33 |
34 |
35 | true
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 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 | 1483618058798
301 |
302 | 1483618058798
303 |
304 |
305 | 1483720371347
306 |
307 |
308 | 1483720371347
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ZABBIX可以实现短信、邮件、微信等各种报警,这三种基本大家都很熟悉,但是多多少少受到一些限制,最近受人启发,说可以实现钉钉报警,然后网上找了找,有GO语言大神写得,无奈自己只会三P,所以就用PY交易来实现,苍老师说过:Life is short,you need python!
2 |
3 | [TOC]
4 |
5 | ### 1 钉钉配置
6 | 钉钉官网:https://oa.dingtalk.com/
7 | 我们主要获取四个参数:部门名称,AgentID和CorpID和CorpSecret
8 | #### 1.1 注册安装
9 | 注册钉钉企业号,安装手机钉钉略过
10 | ##### 1.1.1 部门设置
11 | 在通信录管理里面设置部门,如下图,我们这里设置的**运维部**,这个名称要记住,在ZABBIX里面要配置这个名称,然后把你需要发送告警的人员添加到这个部门里面
12 | 
13 | ##### 1.1.2 应用设置
14 | 在企业应用里面有各种应用,如图,发送告警信息可以给下面的各种应用,如钉邮、钉盘
15 | 
16 | 这里我自己定义了一个应用,服务器报警,记住图中的AgentID,用钉邮、钉盘的AgentID也可以
17 | 
18 | ##### 1.1.3 微应用设置
19 | 找到微应用设置,企业应用,CorpID和CorpSecret,点击获取
20 | 
21 | ### 2 程序配置
22 | 代码托管到github:https://github.com/bluetom520/dingding
23 | ```
24 | git clone https://github.com/bluetom520/dingding.git
25 | pip install requests/requests-2.12.4-py2.py3-none-any.whl
26 | cp dingding/* /usr/lib/zabbix/alertscripts/
27 | chown -R zabbix:zabbix /usr/lib/zabbix/alertscripts/dingding.py
28 | chmod +x /usr/lib/zabbix/alertscripts/dingding.py
29 | chmod a+w /usr/lib/zabbix/alertscripts/config.ini
30 |
31 | ```
32 | 修改config.ini,把上节获得的三个参数填入,toparty不用填,程序第一次运行会自动获取,web是点击报警信息后跳转的页面,大家用自己,不要老给我发。
33 | ```
34 | [ding]
35 | corpid = ding31b4af980259953235c2f4657eb6378f
36 | corpsecret = 5tjFK9oKWptDnh473_2hX3Z_DzZoK2uxhQTqzo6tXf7gd5W6zcOdg8yP-FyjnjfJ
37 | agentid = 66029515
38 | toparty =
39 | web = http://192.168.1.199/zabbix/
40 | ```
41 | ### 3 ZABBIX配置
42 | #### 3.1 报警媒介类型
43 | 到管理-》报警媒介类型配置我们的钉钉
44 | 
45 | #### 3.2 配置用户
46 | 到管理-》用户-》报警媒介-》添加,注意填写收件人为我们之前设置的运维部
47 | 
48 | #### 3.3 动作设置
49 | 到配置-》动作-》创建动作(触发器)
50 | - 动作
51 | 
52 | - 条件
53 | 
54 | ```
55 | 服务器:{HOST.NAME}: {TRIGGER.NAME}已恢复!
56 |
57 | {
58 | "告警主机":"{HOST.NAME}",
59 | "告警地址":"{HOST.IP}",
60 | "告警时间":"{EVENT.DATE} {EVENT.TIME}",
61 | "恢复时间":"{EVENT.RECOVERY.DATE} {EVENT.RECOVERY.TIME}",
62 | "告警等级":"{TRIGGER.SEVERITY}",
63 | "告警信息":"{TRIGGER.NAME}",
64 | "监控项目":"{ITEM.NAME}",
65 | "当前状态":"{TRIGGER.STATUS}",
66 | "持续时间":"{EVENT.AGE}",
67 | "事件ID":"{EVENT.ID}",
68 | "监控取值":"{ITEM.LASTVALUE}"
69 | }
70 |
71 | 服务器:{HOST.NAME}发生: {TRIGGER.NAME}故障!
72 |
73 | {
74 | "告警主机":"{HOST.NAME}",
75 | "告警地址":"{HOST.IP}",
76 | "告警时间":"{EVENT.DATE} {EVENT.TIME}",
77 | "告警等级":"{TRIGGER.SEVERITY}",
78 | "告警信息":"{TRIGGER.NAME}",
79 | "监控项目":"{ITEM.NAME}",
80 | "当前状态":"{TRIGGER.STATUS}",
81 | "持续时间":"{EVENT.AGE}",
82 | "事件ID":"{EVENT.ID}",
83 | "监控取值":"{ITEM.LASTVALUE}"
84 | }
85 | ```
86 | - 操作
87 | 
88 |
89 | ### 4 效果展现
90 | 故障图
91 | 
92 | 恢复图
93 | 
94 |
95 | ### 5 docker环境修改
96 | ```
97 | tar zxvf requests-2.12.4.tar.gz
98 | docker cp requests-2.12.4 zabbix:/usr/local/share/zabbix/alertscripts
99 | docker cp dingding zabbix:/usr/local/share/zabbix/alertscripts
100 | docker exec -it zabbix /bin/bash
101 | cd /usr/local/share/zabbix/alertscripts/requests-2.12.4
102 | rm -rf requests-2.12.4
103 | python setup.py install
104 | cd ..
105 | mv dingding/* .
106 | vi config.ini
107 | exit
108 | docker restart zabbix
109 |
110 | ```
111 |
112 | ## 具体内容参考:https://note.gitcloud.cc/blog/post/bluetom520/%E9%92%89%E9%92%89%E6%8A%A5%E8%AD%A6%E6%A8%A1%E6%9D%BF
113 |
114 |
--------------------------------------------------------------------------------
/conf/BLog.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding:utf-8 -*-
3 | # __author__ = 'bluetom'
4 |
5 | import sys
6 | import logging
7 | from logging.handlers import RotatingFileHandler
8 | import os
9 |
10 | #{{{class ColoredFormatter
11 | class ColoredFormatter(logging.Formatter):
12 | '''A colorful formatter.'''
13 |
14 | def __init__(self, fmt = None, datefmt = None):
15 | logging.Formatter.__init__(self, fmt, datefmt)
16 | # Color escape string
17 | COLOR_RED='\033[1;31m'
18 | COLOR_GREEN='\033[1;32m'
19 | COLOR_YELLOW='\033[1;33m'
20 | COLOR_BLUE='\033[1;34m'
21 | COLOR_PURPLE='\033[1;35m'
22 | COLOR_CYAN='\033[1;36m'
23 | COLOR_GRAY='\033[1;37m'
24 | COLOR_WHITE='\033[1;38m'
25 | COLOR_RESET='\033[1;0m'
26 |
27 | # Define log color
28 | self.LOG_COLORS = {
29 | 'DEBUG': '%s',
30 | 'INFO': COLOR_GREEN + '%s' + COLOR_RESET,
31 | 'WARNING': COLOR_YELLOW + '%s' + COLOR_RESET,
32 | 'ERROR': COLOR_RED + '%s' + COLOR_RESET,
33 | 'CRITICAL': COLOR_RED + '%s' + COLOR_RESET,
34 | 'EXCEPTION': COLOR_RED + '%s' + COLOR_RESET,
35 | }
36 |
37 | def format(self, record):
38 | level_name = record.levelname
39 | msg = logging.Formatter.format(self, record)
40 |
41 | return self.LOG_COLORS.get(level_name, '%s') % msg
42 | #}}}
43 | #{{{class Log
44 | class Log(object):
45 |
46 | '''
47 | log
48 | '''
49 | def __init__(self, filename, level="debug", logid="qiueer", mbs=20, count=10, is_console=True):
50 | '''
51 | mbs: how many MB
52 | count: the count of remain
53 | '''
54 | try:
55 | self._level = level
56 | #print "init,level:",level,"\t","get_map_level:",self._level
57 | self._filename = filename
58 | self._logid = logid
59 |
60 | self._logger = logging.getLogger(self._logid)
61 | file_path = os.path.split(self._filename)[0]
62 | if not os.path.exists(file_path):
63 | os.makedirs(file_path)
64 |
65 | if not len(self._logger.handlers):
66 | self._logger.setLevel(self.get_map_level(self._level))
67 |
68 | fmt = '[%(asctime)s] %(levelname)s\n%(message)s'
69 | datefmt = '%Y-%m-%d %H:%M:%S'
70 | formatter = logging.Formatter(fmt, datefmt)
71 |
72 | maxBytes = int(mbs) * 1024 * 1024
73 | file_handler = RotatingFileHandler(self._filename, mode='a',maxBytes=maxBytes,backupCount=count)
74 | self._logger.setLevel(self.get_map_level(self._level))
75 | file_handler.setFormatter(formatter)
76 | self._logger.addHandler(file_handler)
77 |
78 | if is_console == True:
79 | stream_handler = logging.StreamHandler(sys.stderr)
80 | console_formatter = ColoredFormatter(fmt, datefmt)
81 | stream_handler.setFormatter(console_formatter)
82 | self._logger.addHandler(stream_handler)
83 |
84 | except Exception as expt:
85 | print expt
86 |
87 | def tolog(self, msg, level=None):
88 | try:
89 | level = level if level else self._level
90 | level = str(level).lower()
91 | level = self.get_map_level(level)
92 | if level == logging.DEBUG:
93 | self._logger.debug(msg)
94 | if level == logging.INFO:
95 | self._logger.info(msg)
96 | if level == logging.WARN:
97 | self._logger.warn(msg)
98 | if level == logging.ERROR:
99 | self._logger.error(msg)
100 | if level == logging.CRITICAL:
101 | self._logger.critical(msg)
102 | except Exception as expt:
103 | print expt
104 |
105 | def debug(self,msg):
106 | self.tolog(msg, level="debug")
107 |
108 | def info(self,msg):
109 | self.tolog(msg, level="info")
110 |
111 | def warn(self,msg):
112 | self.tolog(msg, level="warn")
113 |
114 | def error(self,msg):
115 | self.tolog(msg, level="error")
116 |
117 | def critical(self,msg):
118 | self.tolog(msg, level="critical")
119 |
120 | def get_map_level(self,level="debug"):
121 | level = str(level).lower()
122 | #print "get_map_level:",level
123 | if level == "debug":
124 | return logging.DEBUG
125 | if level == "info":
126 | return logging.INFO
127 | if level == "warn":
128 | return logging.WARN
129 | if level == "error":
130 | return logging.ERROR
131 | if level == "critical":
132 | return logging.CRITICAL
133 | #}}}
134 | if __name__ == "__main__":
135 | from BLog import Log
136 | debug=False
137 | logpath = "/tmp/test.log"
138 | logger = Log(logpath,level="warn",is_console=True, mbs=5, count=5)
139 |
140 |
141 | logstr="helloworld"
142 | logger.error(logstr)
143 | logger.info(logstr)
144 | logger.warn(logstr)
145 |
--------------------------------------------------------------------------------
/conf/BLog.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluetom520/dingding/a04de8967326594b1b0e8bb9ca6c4b2a629a4130/conf/BLog.pyc
--------------------------------------------------------------------------------
/conf/INIFILES.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding:utf-8 -*-
3 | # __author__ = 'TT'
4 | import sys,os,time
5 | import ConfigParser
6 |
7 |
8 | class Config:
9 | def __init__(self, path):
10 | self.path = path
11 | self.cf = ConfigParser.ConfigParser()
12 | self.cf.read(self.path)
13 |
14 | def get(self, field, key):
15 | result = ""
16 | try:
17 | result = self.cf.get(field, key)
18 | except:
19 | result = ""
20 | return result
21 |
22 | def set(self, field, key, value):
23 | try:
24 | self.cf.set(field, key, value)
25 | self.cf.write(open(self.path,'w'))
26 | except:
27 | return False
28 | return True
29 |
30 |
31 | def read_config(config_file_path, field, key):
32 | cf = ConfigParser.ConfigParser()
33 | try:
34 | cf.read(config_file_path)
35 | result = cf.get(field, key)
36 | except:
37 | sys.exit(1)
38 | return result
39 |
40 |
41 | def write_config(config_file_path, field, key, value):
42 | cf = ConfigParser.ConfigParser()
43 | try:
44 | cf.read(config_file_path)
45 | cf.set(field, key, value)
46 | cf.write(open(config_file_path,'w'))
47 | except:
48 | sys.exit(1)
49 | return True
50 |
51 | if __name__ == "__main__":
52 | config_file_path = 'config.ini'
53 | print read_config(config_file_path, 'baseconf', "host")
54 |
55 | write_config(config_file_path, 'baseconf', "host", '192.168.0.101')
56 |
57 | print read_config(config_file_path, 'baseconf', "host")
--------------------------------------------------------------------------------
/conf/INIFILES.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluetom520/dingding/a04de8967326594b1b0e8bb9ca6c4b2a629a4130/conf/INIFILES.pyc
--------------------------------------------------------------------------------
/conf/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding:utf-8 -*-
3 | # __author__ = 'bluetom'
--------------------------------------------------------------------------------
/conf/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluetom520/dingding/a04de8967326594b1b0e8bb9ca6c4b2a629a4130/conf/__init__.pyc
--------------------------------------------------------------------------------
/conf/file_util.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding:utf-8 -*-
3 | # __author__ = 'bluetom'
4 |
5 | """Package for operations.
6 | """
7 |
8 | import os
9 | if __name__ == '__main__':
10 | import sys
11 | root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
12 | sys.path.insert(0, root_path)
13 |
14 | import re
15 | import shlex
16 |
17 | import subprocess
18 | import sys
19 | import linecache
20 | reload(sys)
21 | sys.setdefaultencoding('utf8')
22 |
23 | # 需要修改,配置文件位置
24 | CONFIG_CFG = '/etc/zabbix/zabbix_agentd.conf'
25 |
26 | #{{{loadconfig
27 |
28 |
29 | def loadconfig(cfgfile=None, detail=False):
30 | """Read config file and parse config item to dict.
31 | """
32 | if not cfgfile:
33 | cfgfile = CONFIG_CFG
34 |
35 | settings = {}
36 | with open(cfgfile) as f:
37 | for line_i, line in enumerate(f):
38 | # line_i[行号],line[每行内容]
39 |
40 | # 删除空白符(包括'\n', '\r', '\t', ' ')
41 | line = line.strip()
42 |
43 | # 跳过空行和注释('# '开头的)
44 | if not line or line.startswith('# '):
45 | continue
46 |
47 | # detect if it's commented
48 | if line.startswith('#'):
49 | line = line.strip('#')
50 | commented = True
51 | if not detail:
52 | continue
53 | else:
54 | commented = False
55 |
56 | # 将行以第一个'='分割
57 | #########################################
58 | fs = re.split('=', line, 1)
59 | if len(fs) != 2:
60 | continue
61 |
62 | item = fs[0].strip()
63 | value = fs[1].strip()
64 |
65 | if settings.has_key(item):
66 | if detail:
67 | count = settings[item]['count'] + 1
68 | if not commented:
69 | settings[item] = detail and {
70 | 'file': cfgfile,
71 | 'line': line_i,
72 | 'value': value,
73 | 'commented': commented,
74 | } or value
75 | else:
76 | count = 1
77 | settings[item] = detail and {
78 | 'file': cfgfile,
79 | 'line': line_i,
80 | 'value': fs[1].strip(),
81 | 'commented': commented,
82 | } or value
83 | if detail:
84 | settings[item]['count'] = count
85 |
86 | return settings
87 |
88 | #}}}
89 | #{{{cfg_get
90 |
91 |
92 | def cfg_get(item, detail=False, config=None):
93 | """Get value of a config item.
94 | """
95 | if not config:
96 | config = loadconfig(detail=detail)
97 | if config.has_key(item):
98 | return config[item]
99 | else:
100 | return None
101 | #}}}
102 | #{{{cfg_set
103 |
104 |
105 | def cfg_set(item, value, commented=False, config=None):
106 | """Set value of a config item.
107 | 如果可以获取到key,则对key后的item进行修改
108 | 如果获取不到key,则直接在配置文件后进行追加一行
109 | """
110 | cfgfile = CONFIG_CFG
111 | v = cfg_get(item, detail=True, config=config)
112 | # print v
113 |
114 | if v:
115 | # detect if value change
116 | if v['commented'] == commented and v['value'] == value:
117 | return True
118 |
119 | # empty value should be commented
120 | # 如果有key,但是传的value值为空,会将此行进行注释
121 | if value == '':
122 | commented = True
123 |
124 | # replace item in line
125 | lines = []
126 | with open(v['file']) as f:
127 | for line_i, line in enumerate(f):
128 | if line_i == v['line']:
129 | # 对没注释的行进行操作
130 | if not v['commented']:
131 | # 检测是否需要注释
132 | if commented:
133 | if v['count'] > 1:
134 | # delete this line, just ignore it
135 | pass
136 | else:
137 | # comment this line
138 | #########################################
139 | # lines.append('#%s=%s\n' % (item, value))
140 | lines.append('#%s\n' % (line,))
141 | else:
142 | #########################################
143 | lines.append('%s=%s\n' % (item, value))
144 | else:
145 | if commented:
146 | # do not allow change comment value
147 | lines.append(line)
148 | pass
149 | else:
150 | # append a new line after comment line
151 | # lines.append(line)
152 | #########################################
153 | lines.append('%s=%s\n' % (item, value))
154 | else:
155 | lines.append(line)
156 | with open(v['file'], 'w') as f:
157 | f.write(''.join(lines))
158 | else:
159 | # append to the end of file
160 | with open(v['file'], 'a') as f:
161 | f.write('\n%s%s = %s\n' % (commented and '#' or '', item, value))
162 |
163 | return True
164 | #}}}
165 | if __name__ == '__main__':
166 | # import pprint
167 | # pp = pprint.PrettyPrinter(indent=4)
168 | # loadconfig()
169 | # pp.pprint(loadconfig())
170 | print cfg_get('Hostname') #有#号的不显示
171 | # print cfg_get('Subsystem', detail=True) #detail显示细节
172 | # print cfg_set('Subsystem','') #直接注释
173 | # print cfg_set('Subsystem', '44444333') #设置值
174 | # # print cfg_set('Subsystem', 'sftp\t/usr/libexec/openssh/sftp-server',commented=False)
175 | # print cfg_get('Subsystem', detail=True) #detail显示细节
176 |
177 |
--------------------------------------------------------------------------------
/config.ini:
--------------------------------------------------------------------------------
1 | [ding]
2 | corpid = ding31b4af980259953235c2f4657eb6378f
3 | corpsecret = 5tjFK9oKWptDnh473_2hX3Z_DzZoK2uxhQTqzo6tXf7gd5W6zcOdg8yP-FyjnjfJ
4 | agentid = 66029515
5 | toparty =
6 | web = http://192.168.1.199/zabbix/
7 |
8 |
--------------------------------------------------------------------------------
/dingding.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding:utf-8 -*-
3 | # __author__ = '懒懒的天空'
4 |
5 | import requests
6 | import sys
7 | import json
8 | from conf.INIFILES import read_config, write_config
9 | import os
10 | import datetime
11 | from conf.BLog import Log
12 | reload(sys)
13 | sys.setdefaultencoding('utf-8')
14 |
15 |
16 | class Dingtalk(object):
17 | def __init__(self, corpid, corpsecret): # 初始化的时候需要获取corpid和corpsecret,需要从管理后台获取
18 | self.__params = {
19 | 'corpid': corpid,
20 | 'corpsecret': corpsecret
21 | }
22 |
23 | self.url_get_token = 'https://oapi.dingtalk.com/gettoken'
24 | self.url_get_dept_list = 'https://oapi.dingtalk.com/department/list'
25 | self.url_get_dept_detail = 'https://oapi.dingtalk.com/department/get'
26 | self.url_create_dept = 'https://oapi.dingtalk.com/department/create'
27 | self.url_delete_dept = 'https://oapi.dingtalk.com/department/delete'
28 | self.url_get_user_id_by_unionid = 'https://oapi.dingtalk.com/user/getUseridByUnionid'
29 | self.url_get_user_detail = 'https://oapi.dingtalk.com/user/get'
30 | self.url_send_message = 'https://oapi.dingtalk.com/message/send_to_conversation'
31 | self.url_send = 'https://oapi.dingtalk.com/message/send'
32 | self.url_get_user_count = 'https://oapi.dingtalk.com/user/get_org_user_count'
33 | self.__token = self.__get_token()
34 | self.__token_params = {
35 | 'access_token': self.__token
36 | }
37 |
38 | def __raise_error(self, res):
39 | raise Exception('error code: %s,error message: %s' % (res.json()['errcode'], res.json()['errmsg']))
40 | global senderr
41 | sendstatus = False
42 | senderr = 'error code: %s,error message: %s' % (res.json()['errcode'], res.json()['errmsg'])
43 |
44 | def __get_token(self):
45 | # print self.url_get_token
46 | headers = {'content-type': 'application/json'}
47 | res = requests.get(self.url_get_token, headers=headers, params=self.__params)
48 |
49 | try:
50 | return res.json()['access_token']
51 | except:
52 | self.__raise_error(res.content)
53 |
54 | def get_dept_list(self):
55 | # print self.url_get_dept_list
56 | res = requests.get(self.url_get_dept_list, params=self.__token_params)
57 | try:
58 | return res.json()['department']
59 | except:
60 | self.__raise_error(res.content)
61 |
62 | def get_dept_detail(self, dept_id):
63 | params = self.__token_params
64 | params.update({'id': dept_id})
65 | res = requests.get(self.url_get_dept_detail, params=params)
66 | try:
67 | return res.json()
68 | except:
69 | self.__raise_error(res)
70 |
71 | def create_dept(self, name, parentid, orderid, createdeptgroup=False):
72 | payload = self.__token_params
73 | payload.update({
74 | 'name': name,
75 | 'parentid': parentid,
76 | 'orderid': orderid,
77 | 'createDeptGroup': createdeptgroup,
78 | })
79 | res = requests.post(self.url_create_dept, data=payload)
80 | try:
81 | return res.json()['id']
82 | except:
83 | self.__raise_error(res)
84 |
85 | def delete_dept(self, dept_id):
86 | params = self.__token_params
87 | params.update({'id': dept_id})
88 | res = requests.get(self.url_delete_dept, params=params)
89 | try:
90 | return res.json()['errcode']
91 | except:
92 | self.__raise_error(res)
93 |
94 | def get_userid_by_unionid(self, unionid):
95 | params = self.__token_params
96 | params.update({'unionid': unionid})
97 | res = requests.get(self.url_get_user_id_by_unionid, params=params)
98 | try:
99 | return res.json()['userid']
100 | except:
101 | self.__raise_error(res)
102 |
103 | def get_user_detail(self, userid):
104 | params = self.__token_params
105 | params.update({'userid': userid})
106 | res = requests.get(self.url_get_user_detail, params=params)
107 | try:
108 | return res.json()
109 | except:
110 | self.__raise_error(res)
111 |
112 | def send_message(self, agentid, messages, userid='', toparty=''):
113 | payload = {
114 | 'touser': userid,
115 | 'toparty': toparty,
116 | 'agentid': agentid,
117 |
118 | "msgtype": "oa",
119 | "oa": messages
120 | }
121 | headers = {'content-type': 'application/json'}
122 | params = self.__token_params
123 | res = requests.post(self.url_send, headers=headers, params=params, data=json.dumps(payload))
124 | try:
125 | return res.json()
126 | except:
127 | self.__raise_error(res)
128 |
129 | def get_user_count(self, only_active=0):
130 | params = self.__token_params
131 | params.update({'onlyActive': only_active})
132 | res = requests.get(self.url_get_user_count, params=params)
133 | try:
134 | return res.json()['count']
135 | except:
136 | self.__raise_error(res)
137 |
138 |
139 | def main(send_to, subject, content):
140 | try:
141 | global sendstatus
142 | global senderr
143 | data = ''
144 | messages = {}
145 | body = {}
146 | config_file_path = get_path()
147 | CorpID = read_config(config_file_path, 'ding', "CorpID")
148 | CorpSecret = read_config(config_file_path, 'ding', "CorpSecret")
149 | agentid = read_config(config_file_path, 'ding', "agentid")
150 | web = read_config(config_file_path, 'ding', "web")
151 | toparty = read_config(config_file_path, 'ding', "toparty")
152 | content = json.loads(content)
153 | messages["message_url"] = web
154 | form = []
155 |
156 | body["content"] = subject
157 | body["rich"] = {"num": content[u'监控取值']}
158 |
159 | if content[u'当前状态'] == 'PROBLEM':
160 | messages["head"] = {
161 | "bgcolor": "DBE97659", #前两位表示透明度
162 | "text": "服务器故障"
163 | }
164 | body["title"] = "服务器故障"
165 | form.append({'key': '告警等级:', 'value': content[u'告警等级']})
166 | form.append({'key': '告警时间:', 'value': content[u'告警时间']})
167 | form.append({'key': '告警地址:', 'value': content[u'告警地址']})
168 | form.append({'key': '持续时间:', 'value': content[u'持续时间']})
169 | form.append({'key': '监控项目:', 'value': content[u'监控项目']})
170 | body['form'] = form
171 | body["author"] = content[u'告警主机'] + '故障(' + content[u'事件ID']+ ')'
172 | else:
173 | messages["head"] = {
174 | "bgcolor": "DB00AA00", #前两位表示透明度
175 | "text": "服务器恢复"
176 | }
177 | body["title"] = "服务器恢复"
178 | form.append({'key': '告警等级:', 'value': content[u'告警等级']})
179 | form.append({'key': '恢复时间:', 'value': content[u'恢复时间']})
180 | form.append({'key': '告警地址:', 'value': content[u'告警地址']})
181 | form.append({'key': '持续时间:', 'value': content[u'持续时间']})
182 | form.append({'key': '监控项目:', 'value': content[u'监控项目']})
183 | body['form'] = form
184 | body["author"] = content[u'告警主机'] + '恢复(' + content[u'事件ID']+ ')'
185 | messages['body'] = body
186 | ding = Dingtalk(CorpID, CorpSecret)
187 | if toparty == '':
188 | res = ding.get_dept_list()
189 | # topartname = read_config(config_file_path, 'ding', "topartname")
190 | for item in res:
191 | if item['name'] == send_to:
192 | toparty = item['id']
193 | write_config(config_file_path, 'ding', "toparty", toparty)
194 | # print ding.get_dept_list()[1] #65875499钉邮 66029515 自定义 65875504钉盘
195 | data = ding.send_message(toparty=toparty, agentid=agentid, messages=messages)
196 | sendstatus = True
197 | except Exception, e:
198 | senderr = str(e)
199 | sendstatus = False
200 |
201 | logwrite(sendstatus, data)
202 |
203 |
204 | def get_path():
205 | path = os.path.dirname(os.path.abspath(sys.argv[0]))
206 | config_path = path + '/config.ini'
207 | return config_path
208 |
209 |
210 | def logwrite(sendstatus, content):
211 | logpath = '/var/log/zabbix/dingding'
212 | if not sendstatus:
213 | content = senderr
214 | t = datetime.datetime.now()
215 | daytime = t.strftime('%Y-%m-%d')
216 | daylogfile = logpath+'/'+str(daytime)+'.log'
217 | logger = Log(daylogfile, level="info", is_console=False, mbs=5, count=5)
218 | os.system('chown zabbix.zabbix {0}'.format(daylogfile))
219 | logger.info(content)
220 |
221 | if __name__ == "__main__":
222 | if len(sys.argv) > 1:
223 | send_to = sys.argv[1]
224 | subject = sys.argv[2]
225 | content = sys.argv[3]
226 | logwrite(True, content)
227 | main(send_to, subject, content)
228 |
229 |
--------------------------------------------------------------------------------
/log/2017-01-06.log:
--------------------------------------------------------------------------------
1 | INFO:root:**********************************************************************************************************************************
2 | DEBUG:root:{u'invaliduser': u'', u'invalidparty': u'', u'errcode': 0, u'errmsg': u'ok', u'messageId': u'44004de5b8683acab9b1700f56bfb880'}
3 | INFO:root:**********************************************************************************************************************************
4 | DEBUG:root:{u'forbiddenUserId': u'manager1522|07523835553556498', u'invalidparty': u'', u'errcode': 0, u'invaliduser': u'', u'messageId': u'b7277e1ee317357389a5290358aaf0ec', u'errmsg': u'ok'}
5 | INFO:root:**********************************************************************************************************************************
6 | DEBUG:root:{u'forbiddenUserId': u'manager1522|07523835553556498', u'invalidparty': u'', u'errcode': 0, u'invaliduser': u'', u'messageId': u'51b94e481a493951848ce6956bf1fb44', u'errmsg': u'ok'}
7 | INFO:root:**********************************************************************************************************************************
8 | DEBUG:root:{u'invaliduser': u'', u'invalidparty': u'', u'errcode': 0, u'errmsg': u'ok', u'messageId': u'd3fc2bcb9486345fa0684a6e63da0988'}
9 |
--------------------------------------------------------------------------------
/requests/requests-2.12.4-py2.py3-none-any.whl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluetom520/dingding/a04de8967326594b1b0e8bb9ca6c4b2a629a4130/requests/requests-2.12.4-py2.py3-none-any.whl
--------------------------------------------------------------------------------
/requests/requests-2.12.4.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluetom520/dingding/a04de8967326594b1b0e8bb9ca6c4b2a629a4130/requests/requests-2.12.4.tar.gz
--------------------------------------------------------------------------------
/恢复.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluetom520/dingding/a04de8967326594b1b0e8bb9ca6c4b2a629a4130/恢复.jpg
--------------------------------------------------------------------------------
/报警.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluetom520/dingding/a04de8967326594b1b0e8bb9ca6c4b2a629a4130/报警.jpg
--------------------------------------------------------------------------------