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 TelegramGrá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 |
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 |
98 |
“url” = 'http://127.0.0.1/zabbix' - URL de acesso ao FRONT com "http://"
99 |
“user” = 'Admin'
100 |
“pass” = 'zabbix'
101 |
102 |
103 |
[PathSectionEmail]
104 |
105 |
“smtp_server” = 'smtp.gmail.com:587'
106 |
“mail_user” = 'SeuEmail@gmail.com'
107 |
“mail_pass” = 'SuaSenha'
108 |
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 |
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 = "