├── .fuse_hidden000012f500000001 ├── .idea ├── encodings.xml ├── misc.xml ├── modules.xml ├── weixin.iml └── workspace.xml ├── IMG_20170109_205046.png ├── IMG_20170109_205115.png ├── README.md ├── conf ├── BLog.py ├── BLog.pyc ├── INIFILES.py ├── INIFILES.pyc ├── __init__.py ├── __init__.pyc └── file_util.py ├── config.ini ├── requests ├── requests-2.12.4-py2.py3-none-any.whl └── requests-2.12.4.tar.gz ├── requirements.txt └── weixin.py /.fuse_hidden000012f500000001: -------------------------------------------------------------------------------- 1 | [INFO - 2017-01-09T18:46:22.534Z] GhostDriver - Main - running on port 50482 2 | [INFO - 2017-01-09T18:46:23.441Z] Session [eb1f97d0-d69b-11e6-90a5-dbb5d5b2099d] - page.settings - {"XSSAuditingEnabled":false,"javascriptCanCloseWindows":true,"javascriptCanOpenWindows":true,"javascriptEnabled":true,"loadImages":true,"localToRemoteUrlAccessEnabled":false,"userAgent":"Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1","webSecurityEnabled":true} 3 | [INFO - 2017-01-09T18:46:23.441Z] Session [eb1f97d0-d69b-11e6-90a5-dbb5d5b2099d] - page.customHeaders: - {} 4 | [INFO - 2017-01-09T18:46:23.441Z] Session [eb1f97d0-d69b-11e6-90a5-dbb5d5b2099d] - Session.negotiatedCapabilities - {"browserName":"phantomjs","version":"2.1.1","driverName":"ghostdriver","driverVersion":"1.2.0","platform":"linux-unknown-64bit","javascriptEnabled":true,"takesScreenshot":true,"handlesAlerts":false,"databaseEnabled":false,"locationContextEnabled":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"cssSelectorsEnabled":true,"webStorageEnabled":false,"rotatable":false,"acceptSslCerts":false,"nativeEvents":true,"proxy":{"proxyType":"direct"}} 5 | [INFO - 2017-01-09T18:46:23.441Z] SessionManagerReqHand - _postNewSessionCommand - New Session Created: eb1f97d0-d69b-11e6-90a5-dbb5d5b2099d 6 | -------------------------------------------------------------------------------- /.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/weixin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 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 | 1483953404466 39 | 42 | 43 | 44 | 45 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /IMG_20170109_205046.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluetom520/zabbix-weixin/c40cfb7036b30813bd45b28c1e379b0ea8ca18a6/IMG_20170109_205046.png -------------------------------------------------------------------------------- /IMG_20170109_205115.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluetom520/zabbix-weixin/c40cfb7036b30813bd45b28c1e379b0ea8ca18a6/IMG_20170109_205115.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ZABBIX可以实现短信、邮件、微信等各种报警,这三种基本大家都很熟悉, 现在基于微信写py,感觉钉钉的团队是从微信出来的,变量都不改,太懒了,说可以实现微信报警苍老师说过:Life is short,you need python! 2 | 3 | [TOC] 4 | ### 1 微信配置 5 | 微信公众号官网:https://qy.weixin.qq.com/ 6 | 我们主要获取四个参数:部门id,应用ID和CorpID和CorpSecret 7 | #### 1.1 注册安装 8 | 注册微信企业号,安装手机微信略过 9 | ##### 1.1.1 部门设置 10 | 在通信录管理里面设置部门,如下图,我们这里设置的运维部,这个部门id要记住,在ZABBIX里面要配置这个名称,然后把你需要发送告警的人员添加到这个部门里面 11 | ##### 1.1.2 应用设置 12 | 点击左侧“应用中心”,新建消息型应用,应用名称为“服务器报警”,“应用可见范围”,添加刚刚新建的子部门(运维部),点击“服务器报警”,记录应用ID 13 | ##### 1.1.3 微应用设置 14 | 点击左侧“设置”,权限管理,新建普通管理组,名称填写“服务器报警组”。点击修改“通讯录权限”,勾选管理,点击修改“应用权限”,勾选刚刚创建的“服务器报警”,点击刚刚创建的“服务器报警组”,记录左侧的CorpID与CorpSecret 15 | ### 2 程序配置 16 | 代码托管到github:https://github.com/bluetom520/zabbix-weixin 17 | ``` 18 | git clone https://github.com/bluetom520/zabbix-weixin.git 19 | pip install requests/requests-2.12.4-py2.py3-none-any.whl 20 | cp dingding/* /usr/lib/zabbix/alertscripts/ 21 | chown -R zabbix:zabbix /usr/lib/zabbix/alertscripts/dingding.py 22 | chmod +x /usr/lib/zabbix/alertscripts/dingding.py 23 | chmod a+w /usr/lib/zabbix/alertscripts/config.ini 24 | 25 | ``` 26 | 修改config.ini,把上节获得的三个参数填入,web是点击报警信息后跳转的页面,设置的监控数据的最新出图,但手机浏览器不支持。后面看怎么把图抓下来放到微信报警里面,抓图早已经实现了,看大家有没有兴趣了,有兴趣我加再加:) 27 | ``` 28 | [wei] 29 | corpid = wx3317042c8bcf7551 30 | corpsecret = m0VqePgfDsTbVoFlGSx5-JOCbE5p43rf5G-GC2CqN4Wq2Ce0OJQkgo0JnXMqKypv 31 | agentid = 2 32 | toparty = 33 | web = http://192.168.1.199/zabbix/ 34 | ``` 35 | ### 3 ZABBIX配置 36 | #### 3.1 报警媒介类型 37 | 到管理-》报警媒介类型配置我们的微信 38 | ![](leanote://file/getImage?fileId=587386aa4f1ffe4e59000001) 39 | #### 3.2 配置用户 40 | 到管理-》用户-》报警媒介-》添加,注意填写收件人为我们之前设置的运维部id 2 41 | ![](leanote://file/getImage?fileId=587386cf4f1ffe4e59000002) 42 | #### 3.3 动作设置 43 | 到配置-》动作-》创建动作(触发器) 44 | - 动作 45 | ![](leanote://file/getImage?fileId=587089ffd31d9c3103000006) 46 | ``` 47 | 服务器:{HOST.NAME}: {TRIGGER.NAME}已恢复! 48 | 49 | { 50 | "告警主机":"{HOST.NAME}", 51 | "告警地址":"{HOST.IP}", 52 | "告警时间":"{EVENT.DATE} {EVENT.TIME}", 53 | "恢复时间":"{EVENT.RECOVERY.DATE} {EVENT.RECOVERY.TIME}", 54 | "告警等级":"{TRIGGER.SEVERITY}", 55 | "告警信息":"{TRIGGER.NAME}", 56 | "监控项目":"{ITEM.NAME}", 57 | "当前状态":"{TRIGGER.STATUS}", 58 | "持续时间":"{EVENT.AGE}", 59 | "事件ID":"{EVENT.ID}", 60 | "监控ID":"{ITEM.ID}", 61 | "监控取值":"{ITEM.LASTVALUE}" 62 | } 63 | 64 | 服务器:{HOST.NAME}发生: {TRIGGER.NAME}故障! 65 | 66 | { 67 | "告警主机":"{HOST.NAME}", 68 | "告警地址":"{HOST.IP}", 69 | "告警时间":"{EVENT.DATE} {EVENT.TIME}", 70 | "告警等级":"{TRIGGER.SEVERITY}", 71 | "告警信息":"{TRIGGER.NAME}", 72 | "监控项目":"{ITEM.NAME}", 73 | "当前状态":"{TRIGGER.STATUS}", 74 | "持续时间":"{EVENT.AGE}", 75 | "事件ID":"{EVENT.ID}", 76 | "监控ID":"{ITEM.ID}", 77 | "监控取值":"{ITEM.LASTVALUE}" 78 | } 79 | ``` 80 | - 条件 81 | ![](leanote://file/getImage?fileId=58708a1dd31d9c3103000007) 82 | - 操作 83 | ![](leanote://file/getImage?fileId=587386fb4f1ffe4e59000003) 84 | 85 | ### 4 效果展现 86 | 故障图 87 | ![](leanote://file/getImage?fileId=587388a24f1ffe4e59000004) 88 | 恢复图 89 | ![](leanote://file/getImage?fileId=587388ae4f1ffe4e59000005) 90 | 91 | ### 5 docker环境修改 92 | ``` 93 | tar zxvf requests-2.12.4.tar.gz 94 | docker cp requests-2.12.4 zabbix:/usr/local/share/zabbix/alertscripts 95 | docker cp dingding zabbix:/usr/local/share/zabbix/alertscripts 96 | docker exec -it zabbix /bin/bash 97 | cd /usr/local/share/zabbix/alertscripts/requests-2.12.4 98 | python setup.py install 99 | rm -rf requests-2.12.4 100 | cd .. 101 | mv zabbix-weixin/* . 102 | vi config.ini 103 | exit 104 | docker restart zabbix 105 | 106 | ``` 107 | -------------------------------------------------------------------------------- /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/zabbix-weixin/c40cfb7036b30813bd45b28c1e379b0ea8ca18a6/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/zabbix-weixin/c40cfb7036b30813bd45b28c1e379b0ea8ca18a6/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/zabbix-weixin/c40cfb7036b30813bd45b28c1e379b0ea8ca18a6/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 | [wei] 2 | corpid = wx3317042c8bcf7555 3 | corpsecret = m0VqePgfDsTbVoFlGSx5-JOCbE5p43rf5G-GC2CqN4Wq2Ce0OJQkgo0JnXMqKyQv 4 | agentid = 2 5 | toparty = 6 | web = http://192.168.1.199/zabbix/ 7 | 8 | -------------------------------------------------------------------------------- /requests/requests-2.12.4-py2.py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluetom520/zabbix-weixin/c40cfb7036b30813bd45b28c1e379b0ea8ca18a6/requests/requests-2.12.4-py2.py3-none-any.whl -------------------------------------------------------------------------------- /requests/requests-2.12.4.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluetom520/zabbix-weixin/c40cfb7036b30813bd45b28c1e379b0ea8ca18a6/requests/requests-2.12.4.tar.gz -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /weixin.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 WeiXin(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://qyapi.weixin.qq.com/cgi-bin/gettoken' 24 | self.url_send = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?' 25 | 26 | self.__token = self.__get_token() 27 | self.__token_params = { 28 | 'access_token': self.__token 29 | } 30 | 31 | def __raise_error(self, res): 32 | raise Exception('error code: %s,error message: %s' % (res.json()['errcode'], res.json()['errmsg'])) 33 | global senderr 34 | sendstatus = False 35 | senderr = 'error code: %s,error message: %s' % (res.json()['errcode'], res.json()['errmsg']) 36 | 37 | def __get_token(self): 38 | # print self.url_get_token 39 | headers = {'content-type': 'application/json'} 40 | res = requests.get(self.url_get_token, headers=headers, params=self.__params) 41 | 42 | try: 43 | return res.json()['access_token'] 44 | except: 45 | self.__raise_error(res.content) 46 | 47 | 48 | def send_message(self, agentid, messages, userid='', toparty=''): 49 | payload = { 50 | 'touser': userid, 51 | 'toparty': toparty, 52 | 'agentid': agentid, 53 | "msgtype": "news", 54 | "news": messages 55 | } 56 | headers = {'content-type': 'application/json'} 57 | data = json.dumps(payload, ensure_ascii=False).encode('utf-8') 58 | params = self.__token_params 59 | res = requests.post(self.url_send, headers=headers, params=params, data=data) 60 | try: 61 | return res.json() 62 | except: 63 | self.__raise_error(res) 64 | 65 | 66 | def main(send_to, subject, content): 67 | try: 68 | global sendstatus 69 | global senderr 70 | data = '' 71 | messages = {} 72 | body = {} 73 | config_file_path = get_path() 74 | CorpID = read_config(config_file_path, 'wei', "CorpID") 75 | CorpSecret = read_config(config_file_path, 'wei', "CorpSecret") 76 | agentid = read_config(config_file_path, 'wei', "agentid") 77 | web = read_config(config_file_path, 'wei', "web") 78 | content = json.loads(content) 79 | messages["message_url"] = web 80 | body["url"] = web + "history.php?action=showgraph&itemids[]=" + content[u'监控ID'] 81 | warn_message = '' 82 | if content[u'当前状态'] == 'PROBLEM': 83 | body["title"] = "服务器故障" 84 | warn_message += subject + '\n' 85 | warn_message += '详情:\n' 86 | warn_message += '告警等级:'+ content[u'告警等级'] + '\n' 87 | warn_message += '告警时间:'+ content[u'告警时间'] + '\n' 88 | warn_message += '告警地址:'+ content[u'告警地址'] + '\n' 89 | warn_message += '持续时间:'+ content[u'持续时间'] + '\n' 90 | warn_message += '监控项目:'+ content[u'监控项目'] + '\n' 91 | warn_message += content[u'告警主机'] + '故障(' + content[u'事件ID']+ ')' 92 | else: 93 | body["title"] = "服务器恢复" 94 | warn_message += subject + '\n' 95 | warn_message += '详情:\n' 96 | warn_message += '告警等级:'+ content[u'告警等级'] + '\n' 97 | warn_message += '恢复时间:'+ content[u'恢复时间'] + '\n' 98 | warn_message += '告警地址:'+ content[u'告警地址'] + '\n' 99 | warn_message += '持续时间:'+ content[u'持续时间'] + '\n' 100 | warn_message += '监控项目:'+ content[u'监控项目'] + '\n' 101 | warn_message += content[u'告警主机'] + '恢复(' + content[u'事件ID']+ ')' 102 | body['description'] = warn_message 103 | data = [] 104 | data.append(body) 105 | messages['articles'] = data 106 | wx = WeiXin(CorpID, CorpSecret) 107 | data = wx.send_message(toparty=send_to, agentid=agentid, messages=messages) 108 | sendstatus = True 109 | except Exception, e: 110 | senderr = str(e) 111 | sendstatus = False 112 | logwrite(sendstatus, data) 113 | 114 | 115 | def get_path(): 116 | path = os.path.dirname(os.path.abspath(sys.argv[0])) 117 | config_path = path + '/config.ini' 118 | return config_path 119 | 120 | 121 | def logwrite(sendstatus, content): 122 | logpath = '/var/log/zabbix/weixin' 123 | if not sendstatus: 124 | content = senderr 125 | t = datetime.datetime.now() 126 | daytime = t.strftime('%Y-%m-%d') 127 | daylogfile = logpath+'/'+str(daytime)+'.log' 128 | logger = Log(daylogfile, level="info", is_console=False, mbs=5, count=5) 129 | os.system('chown zabbix.zabbix {0}'.format(daylogfile)) 130 | logger.info(content) 131 | 132 | if __name__ == "__main__": 133 | if len(sys.argv) > 1: 134 | send_to = sys.argv[1] 135 | subject = sys.argv[2] 136 | content = sys.argv[3] 137 | logwrite(True, content) 138 | main(send_to, subject, content) 139 | 140 | --------------------------------------------------------------------------------