├── README.md ├── configScrips.properties ├── emailGraph.py └── emailgraph-teste.py /README.md: -------------------------------------------------------------------------------- 1 | # Doações 2 | 3 | 4 | 5 | 10 | 15 | 20 | 21 |
6 | 7 |

PagSeguro

8 | 9 |
11 | 12 |

PayPal

13 | 14 |
16 | 17 |

PicPay

18 | 19 |
22 | 23 | 30 | 31 | # Email-Graph-ZABBIX_Python 32 | Em caso de dúvida, sugestão ou dificuldade junte-se a nós no Grupo do Telegram Gráfico no Email e Telegram. 33 | 34 | O "How to" foi testado no ZABBIX 2.4, 3.0, 3.2, 3.4, 4.0 e no 4.2 no Debian 8-9/Ubuntu 14-18 e CentOS 6.x e 7, caso não utilize estas distros procure os pacotes descritos para sua necessidade. 35 | 36 | # Sumário 37 | 59 | 60 |
61 | 62 | # Requisitos: 63 | 69 | 70 |

Instale os pacotes

71 |

Debian/Ubuntu

72 |
$ sudo apt-get install -y wget unzip git dos2unix python-pip python-requests
73 |

CentOS 6.x, 7 e 8

74 |
sudo yum install -y epel-release ; sudo yum -y update ; sudo yum install -y wget unzip git dos2unix python-pip python-requests
75 | 76 | 83 | 84 |

Faça o download do script de instalação

85 |
cd /tmp ; wget https://raw.githubusercontent.com/sansaoipb/scripts/master/email.sh -O email.sh ; dos2unix email.sh ; sh email.sh
86 | 87 | OBS:
88 | Existe um local padrão onde fica os scripts, que tem 2 locais possiveis dependendo da forma de instalação do ZABBIX, compilando (/usr/local/share/zabbix/alertscripts/) ou por pacote (/usr/lib/zabbix/alertscripts/), respectivamente.
89 | Ao final da execução do script, ele indicará qual pasta você acessará. 90 | 91 | # Edite os parâmetros: 92 | 93 | Entre no arquivo configScrips.properties e edite os campos abaixo: 94 | 95 |
[PathSection]
96 | 97 | 102 | 103 |
[PathSectionEmail]
104 | 109 | 110 | OBS:
111 | 1 – O usuário que você declarar no campo “user” precisa ter permissão no mínimo de leitura no ambiente.
112 | 2 – Se usar gmail, é preciso alterar o acesso à conta para aplicativos, se tiver "Verificação em duas etapas" ativado, é necessário criar uma "Senha de app", crie 113 | Aqui.
Caso não tenha verificação em duas etapas, pode somente ativar "Acesso a app menos seguros", altere 114 | Aqui.
115 | 116 |

Comando para teste

117 | 118 | Script para realização do teste:
119 | Script, Email.
120 | Ex:
121 |
sudo -u zabbix ./emailgraph-teste.py SeuEmail@Provedor.com 
122 | 123 | 126 | 127 | # Configurando o envio: 128 | 129 | Com o script adicionado no local indicado acima, precisamos realizar algumas configurações no Front do ZABBIX, no "Tipo de Mídia", (em Administração > Tipo de Mídia) e a "Ação" (em Configuração > Ações). 130 | 131 |

Tipo de Mídia

132 | 133 |

Zabbix 2.4

134 |

135 | 136 |

Zabbix 3.0 à 4.2

137 |

138 | 139 | OBS:
140 | 1 – Na versão 3.0, é obrigatório a utilização das macros {ALERT.SENDTO}, {ALERT.SUBJECT} e {ALERT.MESSAGE}, em caso de dúvidas, leia a Documentação 141 | Aqui.

142 | 143 |

Configurando o usuário

144 | 145 |
146 | 147 |

Criando a Ação:

148 | 149 | A “Mensagem Padrão” na aba Ação está sendo executada no formato “HTML”, então você pode realizar a formatação que desejar, somente com uma “exigência”, a primeira linha deve permanecer com as macros/variáveis abaixo ilustradas (as macros/variáveis entre as "#" ), podendo editar da segunda linha em diante. 150 | 151 | Exemplificando sobre o HTML, abaixo começou com um parágrafo, e o {HOST.HOST} coloquei em negrito. 152 |
153 |
154 | 155 |

Imagem da Mensagem na Ação:

156 | 157 |

158 | 159 |
Modelo Mensagem Padrão
160 |
161 | {ITEM.NAME}#{EVENT.ID}#{ITEM.ID}#00C800#3600#
162 | 
163 | Foi detectado um evento no equipamento {HOST.HOST}.
164 | 165 | OBS:
166 | ”00C800” é a cor da linha (verde) em Hex. sem tralha, e ”3600” é o período do gráfico (1h) em segundo. 167 | 168 |

Resultado:

169 | 170 | 171 |
172 |
173 | 174 | # Contribuição 175 | 176 | Neste link você consegue criar de modelos para mensagens HTML, que foi indicado pelo amigo "Mario" @ZXRTI 177 |
178 | Site para criação de modelos HTML 179 | 180 | 181 | 182 | 183 | # Conclusão 184 | 185 | 1 – Este script é para agilizar a análise e ficar visualmente mais agradável o recebimento dos alarmes.

186 | 2 – O script realiza uma consulta API mais ampla, detecta automaticamente se o item é de caracter/log/texto, e não envia o gráfico "sem dados" somente o texto, ele dá "ack" no evento e informa quem foi notificado naquela ação. 187 | 188 | 189 |



190 | -------------------------------------------------------------------------------- /configScrips.properties: -------------------------------------------------------------------------------- 1 | [PathSection] 2 | url=http://127.0.0.1/zabbix 3 | user=Admin 4 | pass=zabbix 5 | height=200 6 | width=900 7 | stime=3600 8 | ack=yes 9 | salutation=yes 10 | 11 | [PathSectionEmail] 12 | salutation.email=yes 13 | email_from= 14 | smtp_server=smtp.gmail.com:587 15 | mail_user=SeuEmail@gmail.com 16 | mail_pass=SuaSenha 17 | 18 | [PathSectionTelegram] 19 | salutation.telegram=yes 20 | path.graph=/tmp -------------------------------------------------------------------------------- /emailGraph.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Envio de gráfico por Email através do ZABBIX (Send zabbix alerts graph mail) 5 | # 6 | # 7 | # Copyright (C) <2016> 8 | # 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Affero General Public License 20 | # along with this program. If not, see . 21 | # 22 | # Contacts: 23 | # Eracydes Carvalho (Sansão Simonton) - NOC Analyst - sansaoipb@gmail.com 24 | # Thiago Paz - NOC Analyst - thiagopaz1986@gmail.com 25 | 26 | import os, sys, re, json, time, smtplib, urllib3 27 | import requests 28 | 29 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 30 | 31 | if sys.version_info < (3, 0): 32 | reload(sys) 33 | sys.setdefaultencoding('utf-8') 34 | from email.MIMEMultipart import MIMEMultipart 35 | from email.MIMEText import MIMEText 36 | from email.MIMEImage import MIMEImage 37 | import ConfigParser 38 | conf = ConfigParser 39 | else: 40 | from email.mime.multipart import MIMEMultipart 41 | from email.mime.text import MIMEText 42 | from email.mime.image import MIMEImage 43 | import configparser 44 | conf = configparser 45 | 46 | class PropertiesReaderX: 47 | config = None 48 | def __init__(self, pathToProperties): 49 | PropertiesReaderX.config = conf.RawConfigParser() 50 | PropertiesReaderX.config.read(pathToProperties) 51 | 52 | def getValue(self, section, key): 53 | # type: (object, object) -> object 54 | return PropertiesReaderX.config.get(section, key) 55 | 56 | def setValue(self, section, key): 57 | PropertiesReaderX.config.set(section, key) 58 | 59 | if sys.platform.startswith('win32') or sys.platform.startswith('cygwin') or sys.platform.startswith('darwin'): # para debug quando estiver no WINDOWS ou MAC 60 | path = os.path.join(os.getcwd(), "{0}") 61 | else: 62 | path = "/usr/local/share/zabbix/alertscripts/" 63 | 64 | if not os.path.exists(path): 65 | path = "/usr/lib/zabbix/alertscripts/{0}" 66 | else: 67 | path = "/usr/local/share/zabbix/alertscripts/{0}" 68 | 69 | # Zabbix settings | Dados do Zabbix ############################################################################################################# 70 | zbx_server = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'url') 71 | zbx_user = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'user') 72 | zbx_pass = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'pass') 73 | 74 | # Graph settings | Configuracao do Grafico ###################################################################################################### 75 | height = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'height') # Graph height | Altura 76 | width = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'width') # Graph width | Largura 77 | 78 | # Ack message | Ack da Mensagem ################################################################################################################ 79 | Ack = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'ack') 80 | 81 | # Salutation | Saudação ######################################################################################################################## 82 | Salutation = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'salutation') 83 | if re.search("(sim|s|yes|y)", str(Salutation).lower()): 84 | hora = int(time.strftime("%H")) 85 | 86 | if hora < 12: 87 | salutation = 'Bom dia' 88 | elif hora >= 18: 89 | salutation = 'Boa noite' 90 | else: 91 | salutation = 'Boa tarde' 92 | else: 93 | salutation = "" 94 | 95 | # Diretórios 96 | # Log path | Diretório do log 97 | projeto = "email" 98 | logName = '{0}Graph.log'.format(projeto) 99 | pathLogs = path.format("log") 100 | arqLog = "{0}".format(os.path.join(pathLogs, logName)) 101 | 102 | if not os.path.exists(pathLogs): 103 | os.makedirs(pathLogs) 104 | 105 | # Mail settings | Configrações de e-mail ####################################################################################################### 106 | email_from = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSectionEmail', 'email_from') 107 | smtp_server = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSectionEmail', 'smtp_server') 108 | mail_user = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSectionEmail', 'mail_user') 109 | mail_pass = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSectionEmail', 'mail_pass') 110 | 111 | ################################################################################################################################################# 112 | ################################################################################################################################################# 113 | ################################################################################################################################################# 114 | ################################################################################################################################################# 115 | 116 | import logging.config 117 | import traceback 118 | 119 | file = """{ 120 | "version": 1, 121 | "disable_existing_loggers": false, 122 | "formatters": { 123 | "simple": { 124 | "format": "[%(asctime)s][%(levelname)s] - %(message)s" 125 | } 126 | }, 127 | 128 | "handlers": { 129 | "file_handler": { 130 | "class": "logging.handlers.RotatingFileHandler", 131 | "maxBytes": 5242880, 132 | "backupCount":5, 133 | "level": "DEBUG", 134 | "formatter": "simple", 135 | "filename": "python_logging.log", 136 | "encoding": "utf8" 137 | } 138 | }, 139 | 140 | "root": { 141 | "level": "DEBUG", 142 | "handlers": ["file_handler"] 143 | } 144 | } 145 | """ 146 | 147 | arqConfig = "logging_configuration.json" 148 | pathDefault = "" 149 | 150 | class Log: 151 | @staticmethod 152 | def writelog(entry, pathfile, log_level): 153 | global pathDefault 154 | 155 | try: 156 | Log.log(entry, pathfile, log_level) 157 | except Exception: 158 | try: 159 | if "\\" in traceback.format_exc(): 160 | linha = re.search("(File)[A-Za-z0-9_\"\\\\\s:.]+", traceback.format_exc()).group()[5:].replace("\"","") 161 | pathDefault = "{0}\\".format("\\".join(linha.split("\\")[:-1])) 162 | else: 163 | linha = re.search("(File)[A-Za-z0-9_\"/\s:.]+", traceback.format_exc()).group()[5:].replace("\"", "") 164 | pathDefault = "{0}/".format("/".join(linha.split("/")[:-1])) 165 | arquivo = open("{0}{1}".format(pathDefault, arqConfig), "w") 166 | arquivo.writelines(file) 167 | arquivo.close() 168 | Log.log(entry, pathfile, log_level) 169 | except Exception: 170 | pass 171 | 172 | @staticmethod 173 | def log(entry, pathfile, log_level): 174 | logging.getLogger('suds.client').setLevel(logging.CRITICAL) 175 | logging.getLogger('suds.wsdl').setLevel(logging.CRITICAL) 176 | with open("{0}{1}".format(pathDefault, arqConfig), 'r+') as logging_configuration_file: 177 | config_dict = json.load(logging_configuration_file) 178 | config_dict["handlers"]["file_handler"]['filename'] = pathfile 179 | logging.config.dictConfig(config_dict) 180 | logger = logging.getLogger(__name__) 181 | logging.getLogger("suds").setLevel(logging.CRITICAL) 182 | if log_level.upper() == "INFO": 183 | logger.info(str(entry)) 184 | elif log_level.upper() == "WARNING": 185 | logger.warning(str(entry)) 186 | elif log_level.upper() == "CRITICAL": 187 | logger.critical(str(entry)) 188 | elif log_level.upper() == "ERROR": 189 | logger.error(str(entry)) 190 | 191 | log = Log 192 | 193 | nograph = "nograph" 194 | 195 | if nograph not in sys.argv: 196 | try: 197 | itemname, eventid, itemid, color, period, body = sys.argv[3].split('#', 5) 198 | period = int(period) 199 | 200 | except ValueError as e: 201 | if "unpack" in str(e): 202 | log.writelog( 203 | '{0} >> at split (itemname, eventid, itemid, color, period, body) | Quantidade de argumentos insuficientes no split (itemname, eventid, itemid, color, period, body)'.format( 204 | str(e)), arqLog, "ERROR") 205 | 206 | else: 207 | log.writelog('{0}'.format(str(e)), arqLog, "ERROR") 208 | exit() 209 | 210 | else: 211 | body = "\n{0}".format(sys.argv[3]) 212 | 213 | body = re.sub(r'(\d{4})\.(\d{2})\.(\d{2})', r'\3/\2/\1', body) 214 | 215 | 216 | def destinatarios(dest): 217 | destinatario = ["{0}".format(hostsW).lower().replace(" ", "") for hostsW in dest.split(",")] 218 | 219 | return destinatario 220 | 221 | def send_mail(dest, itemType, get_graph): 222 | dests = ', '.join(dest) 223 | msg = body 224 | msg = msg.replace("\\n", "").replace("\n", "
") 225 | 226 | msgRoot = MIMEMultipart('related') 227 | msgRoot['Subject'] = sys.argv[2] 228 | msgRoot['From'] = email_from 229 | msgRoot['To'] = dests 230 | 231 | msgAlternative = MIMEMultipart('alternative') 232 | msgRoot.attach(msgAlternative) 233 | 234 | saudacao = salutation 235 | Saudacao = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSectionEmail', 'salutation.email') 236 | 237 | if re.search("(sim|s|yes|y)", str(Saudacao).lower()): 238 | if saudacao: 239 | saudacao = "

{0},

".format(salutation) 240 | else: 241 | saudacao = "" 242 | 243 | text = '{0}

{1}

'.format(saudacao, msg) 244 | 245 | if re.search("(0|3)", itemType): 246 | URL = "{0}/history.php?action=showgraph&itemids[]={1}" 247 | text += '
'.format(URL.format(zbx_server, itemid)) 248 | msgImage = MIMEImage(get_graph.content) 249 | msgImage.add_header('Content-ID', '') 250 | msgRoot.attach(msgImage) 251 | 252 | msgText = MIMEText(text, 'html', _charset='utf-8') 253 | msgAlternative.attach(msgText) 254 | 255 | try: 256 | smtp = smtplib.SMTP(smtp_server) 257 | smtp.ehlo() 258 | 259 | try: 260 | smtp.starttls() 261 | except Exception: 262 | pass 263 | 264 | try: 265 | smtp.login(mail_user, mail_pass) 266 | except smtplib.SMTPAuthenticationError as msg: 267 | # print("Error: Unable to send email | Não foi possível enviar o e-mail - {0}".format(msg.smtp_error.decode("utf-8").split(". ")[0])) 268 | log.writelog('Error: Unable to send email | Não foi possível enviar o e-mail - {0}'.format(msg.smtp_error.decode("utf-8").split(". ")[0]),arqLog, "WARNING") 269 | smtp.quit() 270 | exit() 271 | except smtplib.SMTPException: 272 | pass 273 | 274 | try: 275 | smtp.sendmail(email_from, dest, msgRoot.as_string()) 276 | except Exception as msg: 277 | # print("Error: Unable to send email | Não foi possível enviar o e-mail - {0}".format(msg.smtp_error.decode("utf-8").split(". ")[0])) 278 | log.writelog('Error: Unable to send email | Não foi possível enviar o e-mail - {0}'.format(msg.smtp_error.decode("utf-8").split(". ")[0]), arqLog, 279 | "WARNING") 280 | smtp.quit() 281 | exit() 282 | 283 | if re.search("(sim|s|yes|y)", str(Ack).lower()): 284 | if nograph not in sys.argv: 285 | ack(dests, "Email enviado com sucesso ({0})") 286 | 287 | # print("Email sent successfully | Email enviado com sucesso ({0})".format(dests)) 288 | log.writelog('Email sent successfully | Email enviado com sucesso ({0})'.format(dests), arqLog, "INFO") 289 | smtp.quit() 290 | except smtplib.SMTPException as msg: 291 | # print("Error: Unable to send email | Não foi possível enviar o e-mail ({0})".format(msg)) 292 | log.writelog('Error: Unable to send email | Não foi possível enviar o e-mail ({0})'.format(msg), arqLog, "WARNING") 293 | logout_api() 294 | smtp.quit() 295 | exit() 296 | 297 | def token(): 298 | try: 299 | login_api = requests.post('%s/api_jsonrpc.php' % zbx_server, headers = {'Content-type': 'application/json'}, verify=False,\ 300 | data = json.dumps( 301 | { 302 | "jsonrpc": "2.0", 303 | "method": "user.login", 304 | "params": { 305 | "user": zbx_user, 306 | "password": zbx_pass 307 | }, 308 | "id": 1 309 | } 310 | ) 311 | ) 312 | 313 | login_api = json.loads(login_api.text.encode('utf-8')) 314 | 315 | if 'result' in login_api: 316 | auth = login_api["result"] 317 | return auth 318 | 319 | elif 'error' in login_api: 320 | print('Zabbix: %s' % login_api["error"]["data"]) 321 | exit() 322 | else: 323 | print(login_api) 324 | exit() 325 | 326 | except ValueError as e: 327 | print('Check declared zabbix URL/IP and try again | Valide a URL/IP do Zabbix declarada e tente novamente\nCurrent: %s' % zbx_server) 328 | log.writelog('Check declared zabbix URL/IP and try again | Valide a URL/IP do Zabbix declarada e tente novamente. (Current: {0})'.format(zbx_server), arqLog, "WARNING") 329 | exit() 330 | except Exception as e: 331 | print(e) 332 | log.writelog('{0}'.format(str(e)), arqLog, "WARNING") 333 | exit() 334 | 335 | def version_api(): 336 | resultado = requests.post('{0}/api_jsonrpc.php'.format(zbx_server), headers = {'Content-type': 'application/json'}, verify=False,\ 337 | data = json.dumps( 338 | { 339 | "jsonrpc": "2.0", 340 | "method": "apiinfo.version", 341 | "params": [], 342 | "id": 5 343 | } 344 | ) 345 | ) 346 | if sys.version_info < (3, 0): 347 | resultado = json.loads(resultado.content.encode('utf-8')) 348 | else: 349 | resultado = json.loads(resultado.content) 350 | 351 | if 'result' in resultado: 352 | resultado = resultado["result"] 353 | return resultado 354 | 355 | def logout_api(): 356 | requests.post('{0}/api_jsonrpc.php'.format(zbx_server), headers = {'Content-type': 'application/json'}, verify=False,\ 357 | data = json.dumps( 358 | { 359 | "jsonrpc": "2.0", 360 | "method": "user.logout", 361 | "params": [], 362 | "auth": auth, 363 | "id": 4 364 | } 365 | ) 366 | ) 367 | 368 | def getgraph(itemname, period): 369 | stime = int(PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'stime')) # Graph start time [3600 = 1 hour ago] | Hora inicial do grafico [3600 = 1 hora atras] 370 | try: 371 | loginpage = requests.get('%s/index.php' % zbx_server, auth=(zbx_user, zbx_pass), verify=False).text 372 | enter = re.search('(.*?)', loginpage) 373 | enter = str(enter.group(1)) 374 | 375 | s = requests.session() 376 | s.post('%s/index.php?login=1' % zbx_server, params={'name': zbx_user, 'password': zbx_pass, 'enter': enter}, verify=False).text 377 | 378 | stime = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time() - stime)) 379 | 380 | if 4.0 > float(version_api()[:3]): 381 | period = "period={0}".format(period) 382 | else: 383 | periodD = period // 86400 384 | segundos_rest = period % 86400 385 | periodH = segundos_rest // 3600 386 | segundos_rest = segundos_rest % 3600 387 | periodM = segundos_rest // 60 388 | 389 | if periodD > 0: 390 | period = "from=now-{0}d-{1}h-{2}m&to=now".format(periodD, periodH, periodM) 391 | itemname = "{0} ({1}d {2}h:{3}m)".format(itemname, periodD, periodH, periodM) 392 | 393 | elif periodD == 0 and periodH == 0: 394 | period = "from=now-{0}m&to=now".format(periodM) 395 | itemname = "{0} ({1}m)".format(itemname, periodM) 396 | 397 | elif periodD == 0 and period % 60 == 0: 398 | period = "from=now-{0}h&to=now".format(periodH) 399 | itemname = "{0} ({1}h)".format(itemname, periodH) 400 | 401 | else: 402 | period = "from=now-{0}h-{1}m&to=now".format(periodH, periodM) 403 | itemname = "{0} ({1}h:{2}m)".format(itemname, periodH, periodM) 404 | 405 | get_graph = s.get( 406 | '{0}/chart3.php?name={1}&{2}&width={3}&height={4}&stime={5}&items[0][itemid]={6}&items[0][drawtype]=5&items[0][color]={7}'.format( 407 | zbx_server, itemname, period, width, height, stime, itemid, color)) 408 | 409 | sid = s.cookies.items()[0][1] 410 | s.post('{0}/index.php?reconnect=1&sid={1}'.format(zbx_server, sid)) 411 | 412 | return get_graph 413 | 414 | except BaseException as e: 415 | log.writelog( 416 | 'Can\'t connect to {0}/index.php | Não foi possível conectar-se à {0}/index.php'.format(zbx_server), arqLog, 417 | "CRITICAL") 418 | logout_api() 419 | exit() 420 | 421 | def ack(dest, message): 422 | if 4.0 > float(version_api()[:3]): 423 | requests.post('{0}/api_jsonrpc.php'.format(zbx_server), headers = {'Content-type': 'application/json'}, verify=False,\ 424 | data = json.dumps( 425 | { 426 | "jsonrpc": "2.0", 427 | "method": "event.acknowledge", 428 | "params": { 429 | "eventids": eventid, 430 | "message": message.format(dest) 431 | }, 432 | "auth": auth, 433 | "id": 3 434 | } 435 | ) 436 | ) 437 | else: 438 | requests.post('{0}/api_jsonrpc.php'.format(zbx_server), headers={'Content-type': 'application/json'}, verify=False,\ 439 | data=json.dumps( 440 | { 441 | "jsonrpc": "2.0", 442 | "method": "event.acknowledge", 443 | "params": { 444 | "eventids": eventid, 445 | "action": 6, 446 | "message": message.format(dest) 447 | }, 448 | "auth": auth, 449 | "id": 3 450 | } 451 | ) 452 | ) 453 | 454 | def getItemType(itemid): 455 | itemtype_api = requests.post('{0}/api_jsonrpc.php'.format(zbx_server), headers={'Content-type': 'application/json'}, 456 | verify=False,\ 457 | data=json.dumps( 458 | { 459 | "jsonrpc": "2.0", 460 | "method": "item.get", 461 | "params": { 462 | "output": ["value_type"], "itemids": itemid, "webitems": itemid 463 | }, 464 | "auth": auth, 465 | "id": 2 466 | } 467 | ) 468 | ) 469 | 470 | itemtype_api = json.loads(itemtype_api.text.encode('utf-8')) 471 | 472 | if itemtype_api["result"]: 473 | item_type = itemtype_api["result"][0]['value_type'] 474 | return item_type 475 | else: 476 | log.writelog( 477 | 'Invalid ItemID or user has no read permission on item/host | ItemID inválido ou usuário sem permissão de leitura no item/host', 478 | arqLog, "WARNING") 479 | logout_api() 480 | exit() 481 | 482 | def main(): 483 | if nograph not in sys.argv: 484 | item_type = getItemType(itemid) 485 | get_graph = getgraph(itemname, period) 486 | else: 487 | item_type = "1" 488 | get_graph = "" 489 | 490 | dest = sys.argv[1] 491 | destino = destinatarios(dest) 492 | emails = [] 493 | for x in destino: 494 | if re.search("^.*@[a-z0-9]+\.[a-z]+(\.[a-z].*)?$", x.lower()): 495 | emails.append(x) 496 | 497 | if [] != emails: 498 | send_mail(emails, item_type, get_graph) 499 | 500 | if __name__ == '__main__': 501 | auth = token() 502 | main() 503 | logout_api() 504 | -------------------------------------------------------------------------------- /emailgraph-teste.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Envio de gráfico por Email através do ZABBIX (Send zabbix alerts graph Mail) 5 | # 6 | # 7 | # Copyright (C) <2016> 8 | # 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Affero General Public License 20 | # along with this program. If not, see . 21 | # 22 | # Contacts: 23 | # Eracydes Carvalho (Sansão Simonton) - NOC Analyst - sansaoipb@gmail.com 24 | # Thiago Paz - NOC Analyst - thiagopaz1986@gmail.com 25 | 26 | import os, sys, re, json, time, smtplib, urllib3 27 | import requests 28 | 29 | if sys.version_info < (3, 0): 30 | from email.MIMEMultipart import MIMEMultipart 31 | from email.MIMEText import MIMEText 32 | from email.MIMEImage import MIMEImage 33 | import ConfigParser 34 | conf = ConfigParser 35 | else: 36 | from email.mime.multipart import MIMEMultipart 37 | from email.mime.text import MIMEText 38 | from email.mime.image import MIMEImage 39 | import configparser 40 | conf = configparser 41 | 42 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 43 | 44 | class PropertiesReaderX: 45 | config = None 46 | def __init__(self, pathToProperties): 47 | PropertiesReaderX.config = conf.RawConfigParser() 48 | PropertiesReaderX.config.read(pathToProperties) 49 | 50 | def getValue(self, section, key): 51 | # type: (object, object) -> object 52 | return PropertiesReaderX.config.get(section, key) 53 | 54 | def setValue(self, section, key): 55 | PropertiesReaderX.config.set(section, key) 56 | 57 | if sys.platform.startswith('win32') or sys.platform.startswith('cygwin') or sys.platform.startswith('darwin'): # para debug quando estiver no WINDOWS ou MAC 58 | path = os.path.join(os.getcwd(), "{0}") 59 | else: 60 | path = "/usr/local/share/zabbix/alertscripts/" 61 | 62 | if not os.path.exists(path): 63 | path = "/usr/lib/zabbix/alertscripts/{0}" 64 | else: 65 | path = "/usr/local/share/zabbix/alertscripts/{0}" 66 | 67 | 68 | # Zabbix settings | Dados do Zabbix ############################################################################################################# 69 | zbx_server = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'url') 70 | zbx_user = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'user') 71 | zbx_pass = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'pass') 72 | 73 | # Graph settings | Configuracao do Grafico ###################################################################################################### 74 | height = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'height') # Graph height | Altura 75 | width = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'width') # Graph width | Largura 76 | 77 | # Salutation | Saudação ######################################################################################################################## 78 | Salutation = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'salutation') 79 | if re.search("(sim|s|yes|y)", str(Salutation).lower()): 80 | hora = int(time.strftime("%H")) 81 | 82 | if hora < 12: 83 | salutation = 'Bom dia' 84 | elif hora >= 18: 85 | salutation = 'Boa noite' 86 | else: 87 | salutation = 'Boa tarde' 88 | else: 89 | salutation = "" 90 | 91 | # Diretórios 92 | # Log path | Diretório do log 93 | projeto = "email" 94 | logName = '{0}graph-teste.log'.format(projeto) 95 | pathLogs = path.format("log") 96 | arqLog = "{0}".format(os.path.join(pathLogs, logName)) 97 | 98 | if not os.path.exists(pathLogs): 99 | os.makedirs(pathLogs) 100 | 101 | # Mail settings | Configrações de e-mail ####################################################################################################### 102 | email_from = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSectionEmail', 'email_from') 103 | smtp_server = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSectionEmail', 'smtp_server') 104 | mail_user = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSectionEmail', 'mail_user') 105 | mail_pass = PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSectionEmail', 'mail_pass') 106 | 107 | ############################################################################################################ 108 | ############################################################################################################ 109 | ############################################################################################################ 110 | ############################################################################################################ 111 | 112 | import logging.config 113 | import traceback 114 | 115 | file = """{ 116 | "version": 1, 117 | "disable_existing_loggers": false, 118 | "formatters": { 119 | "simple": { 120 | "format": "[%(asctime)s][%(levelname)s] - %(message)s" 121 | } 122 | }, 123 | 124 | "handlers": { 125 | "file_handler": { 126 | "class": "logging.handlers.RotatingFileHandler", 127 | "maxBytes": 5242880, 128 | "backupCount":5, 129 | "level": "DEBUG", 130 | "formatter": "simple", 131 | "filename": "python_logging.log", 132 | "encoding": "utf8" 133 | } 134 | }, 135 | 136 | "root": { 137 | "level": "DEBUG", 138 | "handlers": ["file_handler"] 139 | } 140 | } 141 | """ 142 | 143 | arqConfig = "logging_configuration.json" 144 | pathDefault = "" 145 | 146 | class Log: 147 | @staticmethod 148 | def writelog(entry, pathfile, log_level): 149 | global pathDefault 150 | 151 | try: 152 | Log.log(entry, pathfile, log_level) 153 | except Exception: 154 | try: 155 | if "\\" in traceback.format_exc(): 156 | linha = re.search("(File)[A-Za-z0-9_\"\\\\\s:.]+", traceback.format_exc()).group()[5:].replace("\"","") 157 | pathDefault = "{0}\\".format("\\".join(linha.split("\\")[:-1])) 158 | else: 159 | linha = re.search("(File)[A-Za-z0-9_\"/\s:.]+", traceback.format_exc()).group()[5:].replace("\"", "") 160 | pathDefault = "{0}/".format("/".join(linha.split("/")[:-1])) 161 | arquivo = open("{0}{1}".format(pathDefault, arqConfig), "w") 162 | arquivo.writelines(file) 163 | arquivo.close() 164 | Log.log(entry, pathfile, log_level) 165 | except Exception: 166 | pass 167 | 168 | @staticmethod 169 | def log(entry, pathfile, log_level): 170 | logging.getLogger('suds.client').setLevel(logging.CRITICAL) 171 | logging.getLogger('suds.wsdl').setLevel(logging.CRITICAL) 172 | with open("{0}{1}".format(pathDefault, arqConfig), 'r+') as logging_configuration_file: 173 | config_dict = json.load(logging_configuration_file) 174 | config_dict["handlers"]["file_handler"]['filename'] = pathfile 175 | logging.config.dictConfig(config_dict) 176 | logger = logging.getLogger(__name__) 177 | logging.getLogger("suds").setLevel(logging.CRITICAL) 178 | if log_level.upper() == "INFO": 179 | logger.info(str(entry)) 180 | elif log_level.upper() == "WARNING": 181 | logger.warning(str(entry)) 182 | elif log_level.upper() == "CRITICAL": 183 | logger.critical(str(entry)) 184 | elif log_level.upper() == "ERROR": 185 | logger.error(str(entry)) 186 | 187 | log = Log 188 | 189 | nograph = "nograph" 190 | 191 | def destinatarios(dest): 192 | destinatario = ["{0}".format(hostsW).lower().replace(" ", "") for hostsW in dest.split(",")] 193 | 194 | return destinatario 195 | 196 | def send_mail(dest, itemType, get_graph): 197 | dests = ', '.join(dest) 198 | msg = body 199 | msg = msg.replace("\\n", "").replace("\n", "
") 200 | 201 | msgRoot = MIMEMultipart('related') 202 | msgRoot['Subject'] = subject 203 | msgRoot['From'] = email_from 204 | msgRoot['To'] = dests 205 | 206 | msgAlternative = MIMEMultipart('alternative') 207 | msgRoot.attach(msgAlternative) 208 | 209 | saudacao = salutation 210 | if saudacao: 211 | saudacao = "

{0},

".format(salutation) 212 | else: 213 | saudacao = "" 214 | 215 | text = '{0}

{1}

'.format(saudacao, msg) 216 | 217 | if re.search("(0|3)", itemType): 218 | URL = "{0}/history.php?action=showgraph&itemids[]={1}" 219 | text += '
'.format(URL.format(zbx_server, itemid)) 220 | msgImage = MIMEImage(get_graph.content) 221 | msgImage.add_header('Content-ID', '') 222 | msgRoot.attach(msgImage) 223 | 224 | msgText = MIMEText(text, 'html', _charset='utf-8') 225 | msgAlternative.attach(msgText) 226 | 227 | try: 228 | smtp = smtplib.SMTP(smtp_server) 229 | smtp.ehlo() 230 | 231 | try: 232 | smtp.starttls() 233 | except Exception: 234 | pass 235 | 236 | try: 237 | smtp.login(mail_user, mail_pass) 238 | except smtplib.SMTPAuthenticationError as msg: 239 | print("Error: Unable to send email | Não foi possível enviar o e-mail - {0}".format(msg.smtp_error.decode("utf-8").split(". ")[0])) 240 | log.writelog('Error: Unable to send email | Não foi possível enviar o e-mail - {0}'.format(msg.smtp_error.decode("utf-8").split(". ")[0]),arqLog, "WARNING") 241 | smtp.quit() 242 | exit() 243 | except smtplib.SMTPException: 244 | pass 245 | 246 | try: 247 | smtp.sendmail(email_from, dest, msgRoot.as_string()) 248 | except Exception as msg: 249 | print("Error: Unable to send email | Não foi possível enviar o e-mail - {0}".format(msg.smtp_error.decode("utf-8").split(". ")[0])) 250 | log.writelog('Error: Unable to send email | Não foi possível enviar o e-mail - {0}'.format(msg.smtp_error.decode("utf-8").split(". ")[0]), arqLog, 251 | "WARNING") 252 | smtp.quit() 253 | exit() 254 | 255 | print("Email sent successfully | Email enviado com sucesso ({0})".format(dests)) 256 | log.writelog('Email sent successfully | Email enviado com sucesso ({0})'.format(dests), arqLog, "INFO") 257 | smtp.quit() 258 | except smtplib.SMTPException as msg: 259 | print("Error: Unable to send email | Não foi possível enviar o e-mail ({0})".format(msg)) 260 | log.writelog('Error: Unable to send email | Não foi possível enviar o e-mail ({0})'.format(msg), arqLog, "WARNING") 261 | logout_api() 262 | smtp.quit() 263 | exit() 264 | 265 | def token(): 266 | try: 267 | login_api = requests.post('%s/api_jsonrpc.php' % zbx_server, headers = {'Content-type': 'application/json'}, verify=False,\ 268 | data = json.dumps( 269 | { 270 | "jsonrpc": "2.0", 271 | "method": "user.login", 272 | "params": { 273 | "user": zbx_user, 274 | "password": zbx_pass 275 | }, 276 | "id": 1 277 | } 278 | ) 279 | ) 280 | 281 | login_api = json.loads(login_api.text.encode('utf-8')) 282 | 283 | if 'result' in login_api: 284 | auth = login_api["result"] 285 | return auth 286 | 287 | elif 'error' in login_api: 288 | print('Zabbix: %s' % login_api["error"]["data"]) 289 | exit() 290 | else: 291 | print(login_api) 292 | exit() 293 | 294 | except ValueError as e: 295 | print('Check declared zabbix URL/IP and try again | Valide a URL/IP do Zabbix declarada e tente novamente\nCurrent: %s' % zbx_server) 296 | log.writelog('Check declared zabbix URL/IP and try again | Valide a URL/IP do Zabbix declarada e tente novamente. (Current: {0})'.format(zbx_server), arqLog, "WARNING") 297 | exit() 298 | except Exception as e: 299 | print(e) 300 | log.writelog('{0}'.format(str(e)), arqLog, "WARNING") 301 | exit() 302 | 303 | def version_api(): 304 | resultado = requests.post('{0}/api_jsonrpc.php'.format(zbx_server), headers = {'Content-type': 'application/json'}, verify=False,\ 305 | data = json.dumps( 306 | { 307 | "jsonrpc": "2.0", 308 | "method": "apiinfo.version", 309 | "params": [], 310 | "id": 5 311 | } 312 | ) 313 | ) 314 | resultado = json.loads(resultado.content.encode('utf-8')) 315 | if 'result' in resultado: 316 | resultado = resultado["result"] 317 | return resultado 318 | 319 | def logout_api(): 320 | #import ipdb; ipdb.set_trace() 321 | logout = requests.post('%s/api_jsonrpc.php' % zbx_server, headers = {'Content-type': 'application/json'}, verify=False,\ 322 | data = json.dumps( 323 | { 324 | "jsonrpc": "2.0", 325 | "method": "user.logout", 326 | "params": [], 327 | "auth": auth, 328 | "id": 4 329 | } 330 | ) 331 | ) 332 | 333 | def getgraph(): 334 | stime = int(PropertiesReaderX(path.format('configScrips.properties')).getValue('PathSection', 'stime')) # Graph start time [3600 = 1 hour ago] | Hora inicial do grafico [3600 = 1 hora atras] 335 | 336 | try: 337 | loginpage = requests.get('%s/index.php' % zbx_server, auth=(zbx_user, zbx_pass), verify=False).text 338 | enter = re.search('(.*?)', loginpage) 339 | s = requests.session() 340 | 341 | try: 342 | enter = str(enter.group(1)) 343 | s.post('%s/index.php?login=1' % zbx_server, params={'name': zbx_user, 'password': zbx_pass, 'enter': enter}, verify=False).text 344 | except: 345 | pass 346 | 347 | stime = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time() - stime)) 348 | 349 | get_graph = s.get('%s/chart3.php?name=%s&period=%s&width=%s&height=%s&stime=%s&items[0][itemid]=%s&items[0][drawtype]=5&items[0][color]=%s' % (zbx_server, itemname, period, width, height, stime, itemid, color)) 350 | 351 | sid = s.cookies.items()[0][1] 352 | s.post('{0}/index.php?reconnect=1&sid={1}'.format(zbx_server, sid)) 353 | 354 | return get_graph 355 | 356 | except BaseException: 357 | log.writelog( 358 | 'Can\'t connect to {0}/index.php | Não foi possível conectar-se à {0}/index.php'.format(zbx_server), arqLog, 359 | "CRITICAL") 360 | logout_api() 361 | exit() 362 | 363 | def getItemType(): 364 | try: 365 | limit = 1000 366 | itemid = requests.post('%s/api_jsonrpc.php' % zbx_server, headers = {'Content-type': 'application/json'}, verify=False,\ 367 | data = json.dumps( 368 | { 369 | "jsonrpc": "2.0", 370 | "method": "item.get", 371 | "params": { 372 | "output": ["itemid", "name", "lastvalue", "value_type"], 373 | "limit": limit, 374 | "sortfield": "itemid", 375 | "sortorder": "DESC" 376 | }, 377 | "auth": auth, 378 | "id": 3 379 | } 380 | ) 381 | ) 382 | 383 | ValuesItemid = () 384 | ValueItemid = json.loads(itemid.content) 385 | if 'result' in ValueItemid: 386 | resultado = ValueItemid["result"] 387 | for i in range(0, len(resultado)): 388 | if resultado[i]['lastvalue'] != '0' and re.search("(0|3)", resultado[i]['value_type']): 389 | if resultado[i]['lastvalue']: 390 | ValuesItemid += (resultado[i]['itemid'], resultado[i][u'name'], resultado[i]['value_type']) 391 | break 392 | 393 | return ValuesItemid 394 | 395 | except Exception as msg: 396 | print(msg) 397 | 398 | def main(): 399 | global subject, body, itemid, itemname, period, color 400 | try: 401 | try: 402 | itemid, itemname, item_type = getItemType() 403 | except: 404 | print('User has no read permission on environment | Usuário sem permissão de leitura no ambiente') 405 | log.writelog('User has no read permission on environment | Usuário sem permissão de leitura no ambiente',arqLog, "WARNING") 406 | logout_api() 407 | exit() 408 | 409 | color = '00C800' 410 | period = 3600 411 | subject = 'testando o envio com o item:' 412 | body = '{0}'.format(itemname) 413 | 414 | if sys.version_info < (3, 0): 415 | body = itemname.encode('utf-8') 416 | 417 | dest = sys.argv[1] 418 | destino = destinatarios(dest) 419 | 420 | if nograph in sys.argv: 421 | item_type = "1" 422 | get_graph = "" 423 | else: 424 | get_graph = getgraph() 425 | 426 | send_mail(destino, item_type, get_graph) 427 | 428 | except Exception as msg: 429 | print(msg) 430 | 431 | if __name__ == '__main__': 432 | auth = token() 433 | main() 434 | logout_api() 435 | --------------------------------------------------------------------------------