mirror of
https://github.com/outscale/zabbix-super-vision.git
synced 2024-12-22 13:25:21 +01:00
rework of super-server using jinja2
This commit is contained in:
parent
2ba2c988ea
commit
2bef6efea6
@ -1,10 +1,17 @@
|
|||||||
aiohttp==3.5.4
|
aiohttp==3.6.3
|
||||||
|
aiohttp-jinja2==1.5
|
||||||
|
aiosignal==1.3.1
|
||||||
async-timeout==3.0.1
|
async-timeout==3.0.1
|
||||||
attrs==20.3.0
|
attrs==20.3.0
|
||||||
|
cachetools==5.2.1
|
||||||
chardet==3.0.4
|
chardet==3.0.4
|
||||||
|
charset-normalizer==2.1.1
|
||||||
|
frozenlist==1.3.3
|
||||||
idna==2.10
|
idna==2.10
|
||||||
idna-ssl==1.1.0
|
idna-ssl==1.1.0
|
||||||
|
Jinja2==3.1.2
|
||||||
|
MarkupSafe==2.1.1
|
||||||
multidict==4.7.6
|
multidict==4.7.6
|
||||||
typing-extensions==3.7.4.3
|
typing-extensions==3.7.4.3
|
||||||
yarl==1.6.3
|
yarl==1.5.1
|
||||||
zabbix-api==0.5.5
|
zabbix-api==0.5.5
|
||||||
|
13
settings.py
Normal file
13
settings.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
ZABBIX_API = 'http://127.0.0.1:8080'
|
||||||
|
ZABBIX_URL = 'https://zabbix.internal'
|
||||||
|
ZABBIX_LOGIN = 'Admin'
|
||||||
|
ZABBIX_PASS = 'admin'
|
||||||
|
LIMIT = 3000
|
||||||
|
HOSTGROUP = "Zabbix*"
|
||||||
|
SEVERITY = 3
|
||||||
|
TIMEOUT = 20
|
||||||
|
PORT = 8080
|
||||||
|
ZABBIX_SERVERS_CHECK = ['127.0.0.1:10051']
|
||||||
|
COLOR_TEAM = {
|
||||||
|
"Zabbix servers": "#04B404"
|
||||||
|
}
|
@ -1,328 +0,0 @@
|
|||||||
import aiohttp_jinja2
|
|
||||||
import jinja2
|
|
||||||
import aiohttp
|
|
||||||
import settings
|
|
||||||
import time
|
|
||||||
import datetime
|
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
import random
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import asyncio
|
|
||||||
import socket
|
|
||||||
from aiohttp import web
|
|
||||||
from zabbix_api import ZabbixAPI
|
|
||||||
|
|
||||||
stdio_handler = logging.StreamHandler()
|
|
||||||
stdio_handler.setLevel(logging.INFO)
|
|
||||||
_logger = logging.getLogger('aiohttp.access')
|
|
||||||
_logger.addHandler(stdio_handler)
|
|
||||||
_logger.setLevel(logging.INFO)
|
|
||||||
|
|
||||||
def convert_seconds(seconds):
|
|
||||||
time = "a few sec"
|
|
||||||
if seconds >= 60:
|
|
||||||
time = str(round(seconds / 60)) + " min"
|
|
||||||
if seconds > 3600:
|
|
||||||
if round(seconds / 3600) == 1:
|
|
||||||
time = "an hour"
|
|
||||||
else:
|
|
||||||
time = str(round(seconds / 3600)) + " hours"
|
|
||||||
if seconds >= 86400:
|
|
||||||
if round(seconds / 86400) == 1:
|
|
||||||
time = "a day"
|
|
||||||
else:
|
|
||||||
time = str(round(seconds / 86400)) + " days"
|
|
||||||
if seconds >= 2629746:
|
|
||||||
if round(seconds / 2629746) == 1:
|
|
||||||
time = "a month"
|
|
||||||
else:
|
|
||||||
time = str(round(seconds / 2629746)) + " months"
|
|
||||||
if seconds >= 31536000:
|
|
||||||
if round(seconds / 31536000) == 1:
|
|
||||||
time = "a year"
|
|
||||||
else:
|
|
||||||
time = str(round(seconds / 31536000)) + " years"
|
|
||||||
return time
|
|
||||||
|
|
||||||
def zabbix_login():
|
|
||||||
global zapi
|
|
||||||
x = 0
|
|
||||||
|
|
||||||
while x < 10:
|
|
||||||
try:
|
|
||||||
zapi = ZabbixAPI(server=settings.ZABBIX_API, timeout=int(settings.TIMEOUT))
|
|
||||||
zapi.login(settings.ZABBIX_LOGIN, settings.ZABBIX_PASS)
|
|
||||||
except Exception as e:
|
|
||||||
x+=1
|
|
||||||
_logger.error("[ERR] - {} Retry #{}: {}".format(datetime.datetime.now(), x, e))
|
|
||||||
time.sleep(x)
|
|
||||||
continue
|
|
||||||
break
|
|
||||||
if x >= 10:
|
|
||||||
sys.exit("Can't connect to Zabbix API.")
|
|
||||||
|
|
||||||
def zabbix_call(request, method):
|
|
||||||
global zapi
|
|
||||||
x = 0
|
|
||||||
|
|
||||||
while x < 10:
|
|
||||||
try:
|
|
||||||
if method == 'hostgroup':
|
|
||||||
resp = zapi.hostgroup.get(request)
|
|
||||||
elif method == 'triggers':
|
|
||||||
resp = zapi.trigger.get(request)
|
|
||||||
except Exception as e:
|
|
||||||
x+=1
|
|
||||||
_logger.error("[ERR] - {} Retry #{}: {}".format(datetime.datetime.now(), x, e))
|
|
||||||
time.sleep(x)
|
|
||||||
continue
|
|
||||||
break
|
|
||||||
if x >= 10:
|
|
||||||
sys.exit("Can't perform calls to Zabbix API.")
|
|
||||||
else:
|
|
||||||
return resp
|
|
||||||
|
|
||||||
def read_file(file_to_update):
|
|
||||||
if os.path.exists(file_to_update):
|
|
||||||
with open(file_to_update, 'r') as f:
|
|
||||||
out = json.load(f)
|
|
||||||
f.close()
|
|
||||||
return out
|
|
||||||
return False
|
|
||||||
|
|
||||||
def write_file(notes, file_to_update):
|
|
||||||
with open(file_to_update, 'w+') as f:
|
|
||||||
json.dump(notes, f)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def display_notes(request):
|
|
||||||
note_list = []
|
|
||||||
notes = read_file('./data/motd.json')
|
|
||||||
teams = request.match_info.get('teams')
|
|
||||||
if notes:
|
|
||||||
for ts in notes:
|
|
||||||
for note in notes[ts]:
|
|
||||||
if note['team'] in teams or note['team'] == 'all':
|
|
||||||
date_note = datetime.datetime.utcfromtimestamp(int(ts)).strftime('%Y-%m-%d %H:%M:%S')
|
|
||||||
note_list.append({"lvl": note["lvl"], "date": date_note, "msg": note['msg'], "name": note['name'], "ts": ts })
|
|
||||||
return note_list
|
|
||||||
|
|
||||||
def get_hostgroup_color(hostgroup):
|
|
||||||
if hostgroup not in settings.COLOR_TEAM:
|
|
||||||
color = "#%06x;" % random.randint(0, 0xFFFFFF)
|
|
||||||
settings.COLOR_TEAM[hostgroup] = color
|
|
||||||
else:
|
|
||||||
color = settings.COLOR_TEAM[hostgroup]
|
|
||||||
return color
|
|
||||||
|
|
||||||
def get_hostgroups():
|
|
||||||
groups = []
|
|
||||||
|
|
||||||
for hg in settings.HOSTGROUP:
|
|
||||||
request = dict()
|
|
||||||
request['output'] = 'extend'
|
|
||||||
request['search'] = dict()
|
|
||||||
request['search']['name'] = hg
|
|
||||||
request['searchWildcardsEnabled'] = 1
|
|
||||||
resp = zabbix_call(request, 'hostgroup')
|
|
||||||
for x in range(len(resp)):
|
|
||||||
groups.append(resp[x]['name'])
|
|
||||||
return groups
|
|
||||||
|
|
||||||
def get_ttl_hash(seconds=30):
|
|
||||||
return round(time.time() / seconds)
|
|
||||||
|
|
||||||
### CACHE
|
|
||||||
def get_problems(request, ttl_hash = None):
|
|
||||||
del ttl_hash
|
|
||||||
problems = []
|
|
||||||
limit = settings.LIMIT
|
|
||||||
groups = [group.lower() for group in get_hostgroups()]
|
|
||||||
try:
|
|
||||||
zapi.logged_in()
|
|
||||||
except Exception as e:
|
|
||||||
_logger.info('[INFO] - {}: Connection to Zabbix API'.format(datetime.datetime.now()))
|
|
||||||
zabbix_login()
|
|
||||||
|
|
||||||
team_list = settings.HOSTGROUP
|
|
||||||
|
|
||||||
groupids = []
|
|
||||||
for hg in team_list:
|
|
||||||
request = dict()
|
|
||||||
request['output'] = 'extend'
|
|
||||||
request['search'] = dict()
|
|
||||||
request['search']['name'] = hg
|
|
||||||
request['searchWildcardsEnabled'] = 1
|
|
||||||
resp = zabbix_call(request, 'hostgroup')
|
|
||||||
for x in range(len(resp)):
|
|
||||||
groupids.append(int(resp[x]['groupid']))
|
|
||||||
|
|
||||||
request = dict()
|
|
||||||
request['limit'] = limit
|
|
||||||
request['groupids'] = groupids
|
|
||||||
request['monitored'] = 1
|
|
||||||
request['maintenance'] = 0
|
|
||||||
request['active'] = 1
|
|
||||||
request['min_severity'] = settings.SEVERITY
|
|
||||||
request['output'] = "extend"
|
|
||||||
request['expandData'] = 1
|
|
||||||
request['selectHosts'] = "extend"
|
|
||||||
request['selectGroups'] = "extend"
|
|
||||||
request['expandDescription'] = 1
|
|
||||||
request['only_true'] = 1
|
|
||||||
request['skipDependent'] = 1
|
|
||||||
request['withUnacknowledgedEvents'] = 1
|
|
||||||
request['withLastEventUnacknowledged'] = 1
|
|
||||||
request['selectTags'] = "extend"
|
|
||||||
request['filter'] = dict()
|
|
||||||
request['filter']['value'] = 1
|
|
||||||
request['sortfield'] = ["priority","lastchange"]
|
|
||||||
request['sortorder'] = ["DESC"]
|
|
||||||
|
|
||||||
resp = zabbix_call(request, 'triggers')
|
|
||||||
for x in range(len(resp)):
|
|
||||||
data = resp[x]
|
|
||||||
if len(data['hosts']) > 0:
|
|
||||||
# Loop on problems
|
|
||||||
for z in range(len(data['groups'])):
|
|
||||||
if int(data['groups'][z]['groupid']) in groupids:
|
|
||||||
hostgroup = data['groups'][z]['name']
|
|
||||||
color = get_hostgroup_color(hostgroup)
|
|
||||||
since = convert_seconds(int(time.time()) - int(data['lastchange']))
|
|
||||||
problems.append({'description': data['description'], 'host': data['hosts'][0]['host'], 'priority': data['priority'], 'triggerid': data['triggerid'], 'since': since, 'hostgroup': hostgroup, 'color': color, 'lastchange': data['lastchange']})
|
|
||||||
for y in range(len(data['tags'])):
|
|
||||||
tag_value = data['tags'][y]['value']
|
|
||||||
if tag_value.lower() in groups:
|
|
||||||
color = get_hostgroup_color(tag_value)
|
|
||||||
since = convert_seconds(int(time.time()) - int(data['lastchange']))
|
|
||||||
problems.append({'description': data['description'], 'host': data['hosts'][0]['host'], 'priority': data['priority'], 'triggerid': data['triggerid'], 'since': since, 'hostgroup': tag_value, 'color': color, 'lastchange': data['lastchange']})
|
|
||||||
_logger.info('[INFO] - {}: Refresh...'.format(datetime.datetime.now()))
|
|
||||||
return problems
|
|
||||||
|
|
||||||
async def post_note(request):
|
|
||||||
data = await request.post()
|
|
||||||
msg = data['msg']
|
|
||||||
team = data['team']
|
|
||||||
name = data['name']
|
|
||||||
url = data['url']
|
|
||||||
lvl = data['lvl']
|
|
||||||
ts = int(time.time())
|
|
||||||
|
|
||||||
j = {}
|
|
||||||
j[ts] = []
|
|
||||||
j[ts].append({
|
|
||||||
'team': team,
|
|
||||||
'name': name,
|
|
||||||
'msg': msg,
|
|
||||||
'lvl': lvl
|
|
||||||
})
|
|
||||||
|
|
||||||
if os.path.exists('./data/motd.json'):
|
|
||||||
out = read_file('./data/motd.json')
|
|
||||||
out.update(j)
|
|
||||||
write_file(out, './data/motd.json')
|
|
||||||
else:
|
|
||||||
write_file(j, './data/motd.json')
|
|
||||||
_logger.info("[ADD] - {}".format(j))
|
|
||||||
return aiohttp.web.HTTPFound(location=url, text='{}'.format(ts), content_type='text/html')
|
|
||||||
|
|
||||||
async def del_note(request):
|
|
||||||
data = await request.post()
|
|
||||||
note_id = data['note_id']
|
|
||||||
url = data['url']
|
|
||||||
out = read_file('./data/motd.json')
|
|
||||||
_logger.info('[DEL] - {}'.format(out[note_id]))
|
|
||||||
del out[note_id]
|
|
||||||
write_file(out, './data/motd.json')
|
|
||||||
return aiohttp.web.HTTPFound(location=url)
|
|
||||||
|
|
||||||
async def check_servers():
|
|
||||||
while True:
|
|
||||||
_logger.info('[INFO] - Checking Zabbix Servers: {}'.format(settings.ZABBIX_SERVERS_CHECK))
|
|
||||||
j = {}
|
|
||||||
for ip in settings.ZABBIX_SERVERS_CHECK:
|
|
||||||
port = 10051
|
|
||||||
if ':' in ip:
|
|
||||||
i = ip.split(':')
|
|
||||||
ip = i[0]
|
|
||||||
port = int(i[1].strip())
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
sock.settimeout(5)
|
|
||||||
result = sock.connect_ex((ip, port))
|
|
||||||
if result == 0:
|
|
||||||
_logger.info("[INFO] - Port {} OK: {}".format(port, ip))
|
|
||||||
else:
|
|
||||||
_logger.error("[ERR] - Port {} KO: {}".format(port, ip))
|
|
||||||
j[ip] = result
|
|
||||||
sock.close()
|
|
||||||
if os.path.exists('./data/zabbix-servers.json'):
|
|
||||||
out = read_file('./data/zabbix-servers.json')
|
|
||||||
out.update(j)
|
|
||||||
write_file(out, './data/zabbix-servers.json')
|
|
||||||
else:
|
|
||||||
write_file(j, './data/zabbix-servers.json')
|
|
||||||
await asyncio.sleep(60)
|
|
||||||
|
|
||||||
async def start_background_tasks(app):
|
|
||||||
app['dispatch'] = app.loop.create_task(check_servers())
|
|
||||||
|
|
||||||
async def cleanup_background_tasks(app):
|
|
||||||
app['dispatch'].cancel()
|
|
||||||
await app['dispatch']
|
|
||||||
|
|
||||||
@aiohttp_jinja2.template('index.html')
|
|
||||||
def display_alerts(request):
|
|
||||||
|
|
||||||
url = str(request.url)
|
|
||||||
if '/tv/' in url:
|
|
||||||
tv_mode = True
|
|
||||||
else:
|
|
||||||
tv_mode = False
|
|
||||||
|
|
||||||
teams = request.match_info.get('teams')
|
|
||||||
try:
|
|
||||||
zapi.logged_in()
|
|
||||||
except Exception as e:
|
|
||||||
_logger.info('[INFO] - {}: Connection to Zabbix API'.format(datetime.datetime.now()))
|
|
||||||
zabbix_login()
|
|
||||||
|
|
||||||
if teams:
|
|
||||||
team_list = teams.lower().split('+')
|
|
||||||
|
|
||||||
check_servers = read_file('./data/zabbix-servers.json')
|
|
||||||
notes = display_notes(request)
|
|
||||||
problems = get_problems(request, ttl_hash=get_ttl_hash())
|
|
||||||
_logger.info(len(problems))
|
|
||||||
problems = [problem for problem in problems if problem.get('hostgroup').lower() in team_list]
|
|
||||||
problems = sorted(problems,
|
|
||||||
key = lambda i: (i['priority'], i['lastchange']),
|
|
||||||
reverse=True)
|
|
||||||
_logger.info('[NB ALERTS] - {}'.format(len(problems)))
|
|
||||||
context = {'alerts': problems, 'total_alerts': len(problems), 'zabbix_url': settings.ZABBIX_URL, "hostgroups": get_hostgroups(), "notes": notes, "tv_mode": tv_mode, "check_servers": check_servers}
|
|
||||||
return aiohttp_jinja2.render_template(
|
|
||||||
'index.html', request, context)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
app = web.Application()
|
|
||||||
aiohttp_jinja2.setup(app,
|
|
||||||
loader=jinja2.FileSystemLoader('templates'))
|
|
||||||
|
|
||||||
app.add_routes([
|
|
||||||
web.get('/', display_alerts),
|
|
||||||
web.get('/{teams}', display_alerts),
|
|
||||||
web.get('/tv/{teams}', display_alerts),
|
|
||||||
aiohttp.web.post('/post', post_note),
|
|
||||||
aiohttp.web.post('/del', del_note),
|
|
||||||
aiohttp.web.static('/images', 'images'),
|
|
||||||
aiohttp.web.static('/css', 'css'),
|
|
||||||
aiohttp.web.static('/js', 'js')
|
|
||||||
])
|
|
||||||
|
|
||||||
app.on_startup.append(start_background_tasks)
|
|
||||||
app.on_cleanup.append(cleanup_background_tasks)
|
|
||||||
aiohttp.web.run_app(app, port=settings.PORT)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
536
super-server.py
536
super-server.py
@ -1,65 +1,25 @@
|
|||||||
from zabbix_api import ZabbixAPI
|
import aiohttp_jinja2
|
||||||
from os import path
|
import jinja2
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import json
|
|
||||||
import operator
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import aiohttp.web
|
import settings
|
||||||
import json
|
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import os.path
|
|
||||||
import random
|
import random
|
||||||
import argparse
|
import os
|
||||||
import re
|
import json
|
||||||
import config
|
import asyncio
|
||||||
import socket
|
import socket
|
||||||
|
from aiohttp import web
|
||||||
|
from zabbix_api import ZabbixAPI
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
stdio_handler = logging.StreamHandler()
|
stdio_handler = logging.StreamHandler()
|
||||||
stdio_handler.setLevel(logging.INFO)
|
stdio_handler.setLevel(logging.INFO)
|
||||||
_logger = logging.getLogger('aiohttp.access')
|
_logger = logging.getLogger('aiohttp.access')
|
||||||
_logger.addHandler(stdio_handler)
|
_logger.addHandler(stdio_handler)
|
||||||
_logger.setLevel(logging.DEBUG)
|
_logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("--zabbix_ip", help="Zabbix Frontend IP.", required=True)
|
|
||||||
parser.add_argument("--zabbix_url", help="Zabbix URL. Used to build triggers URL.", required=True)
|
|
||||||
parser.add_argument("--alert_limit", help="Number of alerts to retrieve.", required=True)
|
|
||||||
parser.add_argument("--zabbix_hostgroup", help="Search for hostgroups which correspond to this parameter (Wildcard allowed).", required=True)
|
|
||||||
parser.add_argument("--zabbix_min_severity", help="Minimum trigger severity to retrieve.", required=True)
|
|
||||||
parser.add_argument("--zabbix_login", help="Login to connect to the Zabbix API.", required=True)
|
|
||||||
parser.add_argument("--zabbix_pass", help="Password to connect to the Zabbix API.", required=True)
|
|
||||||
parser.add_argument("--list_zabbix_servers", help="List of Zabbix Servers to check", required=True, nargs="+")
|
|
||||||
parser.add_argument("--zabbix_timeout", help="Timeout to the API.", required=True, type=int)
|
|
||||||
parser.add_argument("--port", help="Listen Port.", required=True)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
ZABBIX_API = 'http://'+args.zabbix_ip
|
|
||||||
ZABBIX_FRONTEND = args.zabbix_url + '/'
|
|
||||||
ZABBIX_LOGIN = args.zabbix_login
|
|
||||||
ZABBIX_PASS = args.zabbix_pass
|
|
||||||
LIMIT = args.alert_limit
|
|
||||||
HOSTGROUP = args.zabbix_hostgroup
|
|
||||||
SEVERITY = args.zabbix_min_severity
|
|
||||||
TIMEOUT = args.zabbix_timeout
|
|
||||||
PORT = args.port
|
|
||||||
ZABBIX_SERVERS_CHECK = args.list_zabbix_servers
|
|
||||||
|
|
||||||
_logger.info('\n[ENVIRONMENT VARIABLES]\n[ZABBIX API] - {}'.format(ZABBIX_API))
|
|
||||||
_logger.info('[ZABBIX FRONTEND] - {}'.format(ZABBIX_FRONTEND))
|
|
||||||
_logger.info('[ALERT LIMIT] - {}'.format(LIMIT))
|
|
||||||
_logger.info('[HOSTGROUP] - {}'.format(HOSTGROUP))
|
|
||||||
_logger.info('[MIN SEVERITY] - {}'.format(SEVERITY))
|
|
||||||
_logger.info('[TIMEOUT] - {}'.format(TIMEOUT))
|
|
||||||
_logger.info('[PORT] - {}'.format(PORT))
|
|
||||||
_logger.info('[ZABBIX SERVERS TO CHECK] - {}\n'.format(ZABBIX_SERVERS_CHECK))
|
|
||||||
|
|
||||||
zapi = None
|
|
||||||
|
|
||||||
def convert_seconds(seconds):
|
def convert_seconds(seconds):
|
||||||
time = "a few sec"
|
time = "a few sec"
|
||||||
@ -87,92 +47,160 @@ def convert_seconds(seconds):
|
|||||||
time = str(round(seconds / 31536000)) + " years"
|
time = str(round(seconds / 31536000)) + " years"
|
||||||
return time
|
return time
|
||||||
|
|
||||||
def severity_badge(severity):
|
def zabbix_login():
|
||||||
if severity == 5:
|
global zapi
|
||||||
badge = '<span class="octicon octicon-flame"> </span>'
|
x = 0
|
||||||
elif severity == 4:
|
|
||||||
badge = '<span class="badge badge-pill badge-danger">4</span>'
|
while x < 10:
|
||||||
elif severity == 3:
|
try:
|
||||||
badge = '<span class="badge badge-pill badge-warning">3</span>'
|
zapi = ZabbixAPI(server=settings.ZABBIX_API, timeout=int(settings.TIMEOUT))
|
||||||
elif severity == 2:
|
zapi.login(settings.ZABBIX_LOGIN, settings.ZABBIX_PASS)
|
||||||
badge = '<span class="badge badge-pill badge-primary">2</span>'
|
except Exception as e:
|
||||||
elif severity == 1:
|
x+=1
|
||||||
badge = '<span class="badge badge-pill badge-info">1</span>'
|
_logger.error("[ERR] - {} Retry #{}: {}".format(datetime.datetime.now(), x, e))
|
||||||
|
time.sleep(x)
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
if x >= 10:
|
||||||
|
sys.exit("Can't connect to Zabbix API.")
|
||||||
|
|
||||||
|
def zabbix_call(request, method):
|
||||||
|
global zapi
|
||||||
|
x = 0
|
||||||
|
|
||||||
|
while x < 10:
|
||||||
|
try:
|
||||||
|
if method == 'hostgroup':
|
||||||
|
resp = zapi.hostgroup.get(request)
|
||||||
|
elif method == 'triggers':
|
||||||
|
resp = zapi.trigger.get(request)
|
||||||
|
except Exception as e:
|
||||||
|
x+=1
|
||||||
|
_logger.error("[ERR] - {} Retry #{}: {}".format(datetime.datetime.now(), x, e))
|
||||||
|
time.sleep(x)
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
if x >= 10:
|
||||||
|
sys.exit("Can't perform calls to Zabbix API.")
|
||||||
else:
|
else:
|
||||||
badge = '<span class="badge badge-pill badge-light">0</span>'
|
return resp
|
||||||
return badge
|
|
||||||
|
|
||||||
def severity_css(severity):
|
def read_file(file_to_update):
|
||||||
css = ""
|
if os.path.exists(file_to_update):
|
||||||
if severity == 5:
|
with open(file_to_update, 'r') as f:
|
||||||
css = 'class="disaster"'
|
out = json.load(f)
|
||||||
return css
|
f.close()
|
||||||
|
return out
|
||||||
|
return False
|
||||||
|
|
||||||
def html_response(data):
|
def write_file(notes, file_to_update):
|
||||||
response = ""
|
with open(file_to_update, 'w+') as f:
|
||||||
for x in range(len(data)):
|
json.dump(notes, f)
|
||||||
seconds = int(time.time()) - int(data[x]['lastchange'])
|
f.close()
|
||||||
since = convert_seconds(seconds)
|
|
||||||
css_class = severity_css(int(data[x]['priority']))
|
|
||||||
badge = severity_badge(int(data[x]['priority']))
|
|
||||||
if data[x]['team'] not in config.COLOR_TEAM:
|
|
||||||
color = "#%06x;" % random.randint(0, 0xFFFFFF)
|
|
||||||
config.COLOR_TEAM[data[x]['team']] = color
|
|
||||||
else:
|
|
||||||
color = config.COLOR_TEAM[data[x]['team']]
|
|
||||||
response += "<tr "+css_class+"><td><span class='badge badge-pill' style='background-color:"+color+"'>"+data[x]['team']+"</span></td><td>"+data[x]['host']+"</td><td><a href='"+ZABBIX_FRONTEND+"zabbix.php?action=problem.view&filter_set=1&filter_triggerids%5B%5D="+data[x]['triggerid']+"' target='_blank' "+css_class+">"+data[x]['description']+"</a></td><td>"+badge+"</td><td><span class='badge badge-pill badge-light'>"+since+"</span></td></tr>"
|
|
||||||
|
|
||||||
return response
|
def display_notes(request):
|
||||||
|
note_list = []
|
||||||
|
notes = read_file('./data/motd.json')
|
||||||
|
teams = request.match_info.get('teams')
|
||||||
|
if notes:
|
||||||
|
for ts in notes:
|
||||||
|
for note in notes[ts]:
|
||||||
|
if note['team'] in teams or note['team'] == 'all':
|
||||||
|
date_note = datetime.datetime.utcfromtimestamp(int(ts)).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
note_list.append({"lvl": note["lvl"], "date": date_note, "msg": note['msg'], "name": note['name'], "ts": ts })
|
||||||
|
return note_list
|
||||||
|
|
||||||
def capitalize_nth(s, n):
|
def get_hostgroup_color(hostgroup):
|
||||||
if len(s) == 2:
|
if hostgroup not in settings.COLOR_TEAM:
|
||||||
return s[:n].upper() + s[n:].capitalize()
|
color = "#%06x;" % random.randint(0, 0xFFFFFF)
|
||||||
|
settings.COLOR_TEAM[hostgroup] = color
|
||||||
else:
|
else:
|
||||||
return s.title()
|
color = settings.COLOR_TEAM[hostgroup]
|
||||||
|
return color
|
||||||
|
|
||||||
|
def get_hostgroups():
|
||||||
|
groups = []
|
||||||
|
|
||||||
|
for hg in settings.HOSTGROUP:
|
||||||
|
request = dict()
|
||||||
|
request['output'] = 'extend'
|
||||||
|
request['search'] = dict()
|
||||||
|
request['search']['name'] = hg
|
||||||
|
request['searchWildcardsEnabled'] = 1
|
||||||
|
resp = zabbix_call(request, 'hostgroup')
|
||||||
|
for x in range(len(resp)):
|
||||||
|
groups.append(resp[x]['name'])
|
||||||
|
return groups
|
||||||
|
|
||||||
def construct_menu():
|
def get_ttl_hash(seconds=45):
|
||||||
MENU = ""
|
return round(time.time() / seconds)
|
||||||
|
|
||||||
for key in config.CONTENT.keys():
|
@lru_cache()
|
||||||
MENU += "<li class='nav-item'><a class='nav-link' href='/"+key+"'>"+ capitalize_nth(key.replace('Team-', ''),2) +"</a></li>"
|
def get_problems(ttl_hash = None):
|
||||||
return MENU
|
del ttl_hash
|
||||||
|
problems = []
|
||||||
def construct_form():
|
limit = settings.LIMIT
|
||||||
FORM = "<div class='col'><select class='form-control' name='team'><option value='all'>All teams</option>"
|
groups = [group.lower() for group in get_hostgroups()]
|
||||||
|
try:
|
||||||
for key in config.CONTENT.keys():
|
zapi.logged_in()
|
||||||
FORM += "<option value='"+key+"'>"+key+"</option>"
|
except Exception as e:
|
||||||
FORM += "</select></div>"
|
_logger.info('[INFO] - {}: Connection to Zabbix API'.format(datetime.datetime.now()))
|
||||||
return FORM
|
zabbix_login()
|
||||||
|
|
||||||
async def check_servers():
|
team_list = settings.HOSTGROUP
|
||||||
global ZABBIX_SERVERS_CHECK
|
|
||||||
|
groupids = []
|
||||||
while True:
|
for hg in team_list:
|
||||||
_logger.info('[INFO] - Checking Zabbix Servers: {}'.format(ZABBIX_SERVERS_CHECK))
|
req = dict()
|
||||||
j = {}
|
req['output'] = 'extend'
|
||||||
for ip in ZABBIX_SERVERS_CHECK:
|
req['search'] = dict()
|
||||||
port = 10051
|
req['search']['name'] = hg
|
||||||
if ':' in ip:
|
req['searchWildcardsEnabled'] = 1
|
||||||
i = ip.split(':')
|
resp = zabbix_call(req, 'hostgroup')
|
||||||
ip = i[0]
|
for x in range(len(resp)):
|
||||||
port = int(i[1].strip())
|
groupids.append(int(resp[x]['groupid']))
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
sock.settimeout(5)
|
req = dict()
|
||||||
result = sock.connect_ex((ip, port))
|
req['limit'] = limit
|
||||||
if result == 0:
|
req['groupids'] = groupids
|
||||||
_logger.info("[INFO] - Port {} OK: {}".format(port, ip))
|
req['monitored'] = 1
|
||||||
else:
|
req['maintenance'] = 0
|
||||||
_logger.error("[ERR] - Port {} KO: {}".format(port, ip))
|
req['active'] = 1
|
||||||
j[ip] = result
|
req['min_severity'] = settings.SEVERITY
|
||||||
sock.close()
|
req['output'] = "extend"
|
||||||
if os.path.exists('./data/zabbix-servers.json'):
|
req['expandData'] = 1
|
||||||
out = read_file('./data/zabbix-servers.json')
|
req['selectHosts'] = "extend"
|
||||||
out.update(j)
|
req['selectGroups'] = "extend"
|
||||||
write_file(out, './data/zabbix-servers.json')
|
req['expandDescription'] = 1
|
||||||
else:
|
req['only_true'] = 1
|
||||||
write_file(j, './data/zabbix-servers.json')
|
req['skipDependent'] = 1
|
||||||
await asyncio.sleep(60)
|
req['withUnacknowledgedEvents'] = 1
|
||||||
|
req['withLastEventUnacknowledged'] = 1
|
||||||
|
req['selectTags'] = "extend"
|
||||||
|
req['filter'] = dict()
|
||||||
|
req['filter']['value'] = 1
|
||||||
|
req['sortfield'] = ["priority","lastchange"]
|
||||||
|
req['sortorder'] = ["DESC"]
|
||||||
|
|
||||||
|
resp = zabbix_call(req, 'triggers')
|
||||||
|
for x in range(len(resp)):
|
||||||
|
data = resp[x]
|
||||||
|
if len(data['hosts']) > 0:
|
||||||
|
# Loop on problems
|
||||||
|
for z in range(len(data['groups'])):
|
||||||
|
if int(data['groups'][z]['groupid']) in groupids:
|
||||||
|
hostgroup = data['groups'][z]['name']
|
||||||
|
color = get_hostgroup_color(hostgroup)
|
||||||
|
since = convert_seconds(int(time.time()) - int(data['lastchange']))
|
||||||
|
problems.append({'description': data['description'], 'host': data['hosts'][0]['host'], 'priority': data['priority'], 'triggerid': data['triggerid'], 'since': since, 'hostgroup': hostgroup, 'color': color, 'lastchange': data['lastchange']})
|
||||||
|
for y in range(len(data['tags'])):
|
||||||
|
tag_value = data['tags'][y]['value']
|
||||||
|
if tag_value.lower() in groups:
|
||||||
|
color = get_hostgroup_color(tag_value)
|
||||||
|
since = convert_seconds(int(time.time()) - int(data['lastchange']))
|
||||||
|
problems.append({'description': data['description'], 'host': data['hosts'][0]['host'], 'priority': data['priority'], 'triggerid': data['triggerid'], 'since': since, 'hostgroup': tag_value, 'color': color, 'lastchange': data['lastchange']})
|
||||||
|
_logger.info('[INFO] - {}: Refresh...'.format(datetime.datetime.now()))
|
||||||
|
return problems
|
||||||
|
|
||||||
async def post_note(request):
|
async def post_note(request):
|
||||||
data = await request.post()
|
data = await request.post()
|
||||||
@ -201,19 +229,6 @@ async def post_note(request):
|
|||||||
_logger.info("[ADD] - {}".format(j))
|
_logger.info("[ADD] - {}".format(j))
|
||||||
return aiohttp.web.HTTPFound(location=url, text='{}'.format(ts), content_type='text/html')
|
return aiohttp.web.HTTPFound(location=url, text='{}'.format(ts), content_type='text/html')
|
||||||
|
|
||||||
def read_file(file_to_update):
|
|
||||||
if os.path.exists(file_to_update):
|
|
||||||
with open(file_to_update, 'r') as f:
|
|
||||||
out = json.load(f)
|
|
||||||
return out
|
|
||||||
f.close()
|
|
||||||
return False
|
|
||||||
|
|
||||||
def write_file(notes, file_to_update):
|
|
||||||
with open(file_to_update, 'w+') as f:
|
|
||||||
json.dump(notes, f)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
async def del_note(request):
|
async def del_note(request):
|
||||||
data = await request.post()
|
data = await request.post()
|
||||||
note_id = data['note_id']
|
note_id = data['note_id']
|
||||||
@ -224,195 +239,90 @@ async def del_note(request):
|
|||||||
write_file(out, './data/motd.json')
|
write_file(out, './data/motd.json')
|
||||||
return aiohttp.web.HTTPFound(location=url)
|
return aiohttp.web.HTTPFound(location=url)
|
||||||
|
|
||||||
async def show_alerts(request):
|
async def check_servers():
|
||||||
global CONTENT
|
|
||||||
global NAVBAR
|
|
||||||
global JS_CONTENT
|
|
||||||
global TEMPLATE_FOOTER
|
|
||||||
global TOTAL_ALERTS
|
|
||||||
|
|
||||||
data_list = []
|
|
||||||
html_content = ""
|
|
||||||
html_notes = "<table class='table table-borderless'>"
|
|
||||||
html_check = "<table class='table table-borderless table-sm'>"
|
|
||||||
response = ""
|
|
||||||
|
|
||||||
config.TEMPLATE_HEAD = config.TEMPLATE_HEAD.replace('FORM_TEAM', construct_form())
|
|
||||||
|
|
||||||
config.NAVBAR = config.NAVBAR.replace('LIST', construct_menu())
|
|
||||||
config.NAVBAR = re.sub('\[[0-9]+\]', '['+config.TOTAL_ALERTS+']', config.NAVBAR)
|
|
||||||
|
|
||||||
url = str(request.url)
|
|
||||||
if '/tv/' in url:
|
|
||||||
config.TEMPLATE_FOOTER = config.TEMPLATE_FOOTER.replace('show', 'hide')
|
|
||||||
else:
|
|
||||||
config.TEMPLATE_FOOTER = config.TEMPLATE_FOOTER.replace('hide', 'show')
|
|
||||||
|
|
||||||
teams = request.match_info.get('teams')
|
|
||||||
if not teams:
|
|
||||||
teams = []
|
|
||||||
notes = read_file('./data/motd.json')
|
|
||||||
if notes:
|
|
||||||
for ts in notes:
|
|
||||||
for note in notes[ts]:
|
|
||||||
if note['team'] in teams or note['team'] == 'all':
|
|
||||||
date_note = datetime.datetime.utcfromtimestamp(int(ts)).strftime('%Y-%m-%d %H:%M:%S')
|
|
||||||
html_notes += "<tr class='bg-"+note['lvl']+"'><td class='text-left'><span class='octicon octicon-clock'></span> "+date_note+"</td><td class='text-center'> "+note['msg']+"</td><td class='text-right'><span class='octicon octicon-hubot'></span><i> (by "+note['name']+")</i></td><td class='text-right'><form action='/del' method='post' accept-charset='utf-8' enctype='application/x-www-form-urlencoded'><input type='text' class='form-control url_note' name='url' hidden readonly><input type='text' name='note_id' value='"+ts+"' readonly hidden><button type='submit' class='btn btn-outline-light btn-sm' id='del_note' ><span class='octicon octicon-trashcan'></span></button></form></td></tr>"
|
|
||||||
html_notes += '</table>'
|
|
||||||
check_zbx = read_file('./data/zabbix-servers.json')
|
|
||||||
if check_zbx:
|
|
||||||
for ip in check_zbx:
|
|
||||||
if check_zbx[ip] != 0:
|
|
||||||
html_check += "<tr class='bg-danger'><td class='text-center align-left'><span class='octicon octicon-alert'></span> Zabbix Server: "+ ip + " seems UNREACHABLE! <span class='octicon octicon-alert'></span></td></tr>"
|
|
||||||
html_check += '</table>'
|
|
||||||
IMAGE='<div class="container-fluid image-container"><img src="/images/zabbix_logo_500x131.png" width="30%"/></div>'
|
|
||||||
if teams:
|
|
||||||
team_list = teams.split('+')
|
|
||||||
for team in team_list:
|
|
||||||
team_alerts = config.CONTENT.get(team, {})
|
|
||||||
data_list.extend(team_alerts)
|
|
||||||
if data_list:
|
|
||||||
data = sorted(data_list,
|
|
||||||
key = lambda i: (i['priority'], i['lastchange']),
|
|
||||||
reverse=True)
|
|
||||||
result = html_response(data)
|
|
||||||
html_content = config.TEMPLATE_HEAD.replace('[alerts]', '['+str(len(data))+']').replace('NAVBAR', config.NAVBAR).replace('NOTES', html_notes).replace('CHECK', html_check) + result + config.TEMPLATE_FOOTER
|
|
||||||
else:
|
|
||||||
html_content = config.TEMPLATE_HEAD.replace('NAVBAR', config.NAVBAR).replace('NOTES', html_notes).replace('CHECK', html_check) + '<div class="alert alert-dark text-center" role="alert">No Alerts for '+str(teams)+'! <span class="octicon octicon-thumbsup"></span></div>'+IMAGE+' ' + config.TEMPLATE_FOOTER
|
|
||||||
else:
|
|
||||||
html_content = config.TEMPLATE_HEAD.replace('NAVBAR', config.NAVBAR) + response + config.TEMPLATE_FOOTER
|
|
||||||
return aiohttp.web.Response(text=html_content, content_type='text/html')
|
|
||||||
|
|
||||||
def zabbix_login():
|
|
||||||
global zapi
|
|
||||||
x = 0
|
|
||||||
|
|
||||||
while x < 10:
|
|
||||||
try:
|
|
||||||
zapi = ZabbixAPI(server=ZABBIX_API, timeout=int(TIMEOUT))
|
|
||||||
zapi.login(ZABBIX_LOGIN, ZABBIX_PASS)
|
|
||||||
except Exception as e:
|
|
||||||
x+=1
|
|
||||||
_logger.error("[ERR] - {} Retry #{}: {}".format(datetime.datetime.now(), x, e))
|
|
||||||
time.sleep(x)
|
|
||||||
continue
|
|
||||||
break
|
|
||||||
if x >= 10:
|
|
||||||
sys.exit("Can't connect to Zabbix API.")
|
|
||||||
|
|
||||||
def call_zabbix(request, method):
|
|
||||||
global zapi
|
|
||||||
x = 0
|
|
||||||
|
|
||||||
while x < 10:
|
|
||||||
try:
|
|
||||||
if method == 'hostgroup':
|
|
||||||
resp = zapi.hostgroup.get(request)
|
|
||||||
elif method == 'triggers':
|
|
||||||
resp = zapi.trigger.get(request)
|
|
||||||
except Exception as e:
|
|
||||||
x+=1
|
|
||||||
_logger.error("[ERR] - {} Retry #{}: {}".format(datetime.datetime.now(), x, e))
|
|
||||||
time.sleep(x)
|
|
||||||
continue
|
|
||||||
break
|
|
||||||
if x >= 10:
|
|
||||||
sys.exit("Can't perform calls to Zabbix API.")
|
|
||||||
else:
|
|
||||||
return resp
|
|
||||||
|
|
||||||
async def process_zabbix_queue():
|
|
||||||
global CONTENT
|
|
||||||
global TOTAL_ALERTS
|
|
||||||
global HOSTGROUP
|
|
||||||
global LIMIT
|
|
||||||
global SEVERITY
|
|
||||||
global zapi
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
_logger.info('[INFO] - Checking Zabbix Servers: {}'.format(settings.ZABBIX_SERVERS_CHECK))
|
||||||
zapi.logged_in()
|
j = {}
|
||||||
except Exception as e:
|
for ip in settings.ZABBIX_SERVERS_CHECK:
|
||||||
_logger.info('[INFO] - {}: Connection to Zabbix API'.format(datetime.datetime.now()))
|
port = 10051
|
||||||
zabbix_login()
|
if ':' in ip:
|
||||||
|
i = ip.split(':')
|
||||||
request = dict()
|
ip = i[0]
|
||||||
request['output'] = 'extend'
|
port = int(i[1].strip())
|
||||||
request['search'] = dict()
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
request['search']['name'] = [HOSTGROUP]
|
sock.settimeout(5)
|
||||||
request['searchWildcardsEnabled'] = 1
|
result = sock.connect_ex((ip, port))
|
||||||
resp = call_zabbix(request, 'hostgroup')
|
if result == 0:
|
||||||
groupids = []
|
_logger.info("[INFO] - Port {} OK: {}".format(port, ip))
|
||||||
for x in range(len(resp)):
|
else:
|
||||||
config.CONTENT[resp[x]['name']] = []
|
_logger.error("[ERR] - Port {} KO: {}".format(port, ip))
|
||||||
groupids.append(resp[x]['groupid'])
|
j[ip] = result
|
||||||
|
sock.close()
|
||||||
request = dict()
|
if os.path.exists('./data/zabbix-servers.json'):
|
||||||
request['limit'] = LIMIT
|
out = read_file('./data/zabbix-servers.json')
|
||||||
request['groupids'] = groupids
|
out.update(j)
|
||||||
request['monitored'] = 1
|
write_file(out, './data/zabbix-servers.json')
|
||||||
request['maintenance'] = 0
|
else:
|
||||||
request['active'] = 1
|
write_file(j, './data/zabbix-servers.json')
|
||||||
request['min_severity'] = SEVERITY
|
await asyncio.sleep(60)
|
||||||
request['output'] = "extend"
|
|
||||||
request['expandData'] = 1
|
|
||||||
request['selectHosts'] = "extend"
|
|
||||||
request['selectGroups'] = "extend"
|
|
||||||
request['expandDescription'] = 1
|
|
||||||
request['only_true'] = 1
|
|
||||||
request['skipDependent'] = 1
|
|
||||||
request['withUnacknowledgedEvents'] = 1
|
|
||||||
request['withLastEventUnacknowledged'] = 1
|
|
||||||
request['selectTags'] = "extend"
|
|
||||||
request['filter'] = dict()
|
|
||||||
request['filter']['value'] = 1
|
|
||||||
request['sortfield'] = ["priority","lastchange"]
|
|
||||||
request['sortorder'] = ["DESC"]
|
|
||||||
_logger.info(request)
|
|
||||||
resp = call_zabbix(request, 'triggers')
|
|
||||||
config.TOTAL_ALERTS = str(len(resp))
|
|
||||||
_logger.info('[NB ALERTS] - {}'.format(config.TOTAL_ALERTS))
|
|
||||||
hostgroup_to_search = HOSTGROUP.replace('*','')
|
|
||||||
for x in range(len(resp)):
|
|
||||||
data = resp[x]
|
|
||||||
if len(data['hosts']) > 0:
|
|
||||||
for z in range(len(data['groups'])):
|
|
||||||
if hostgroup_to_search in data['groups'][z]['name']:
|
|
||||||
group = data['groups'][z]['name']
|
|
||||||
config.CONTENT[group].append({'description': data['description'], 'host': data['hosts'][0]['host'], 'priority': data['priority'], 'triggerid': data['triggerid'], 'lastchange': data['lastchange'], 'team': group})
|
|
||||||
for y in range(len(data['tags'])):
|
|
||||||
if 'team' == data['tags'][y]['tag']:
|
|
||||||
team = data['tags'][y]['value']
|
|
||||||
config.CONTENT[team].append({'description': data['description'], 'host': data['hosts'][0]['host'], 'priority': data['priority'], 'triggerid': data['triggerid'], 'lastchange': data['lastchange'], 'team': team})
|
|
||||||
_logger.info('[INFO] - {}: Refresh...'.format(datetime.datetime.now()))
|
|
||||||
await asyncio.sleep(15)
|
|
||||||
|
|
||||||
async def start_background_tasks(app):
|
async def start_background_tasks(app):
|
||||||
app['dispatch'] = app.loop.create_task(process_zabbix_queue())
|
|
||||||
app['dispatch'] = app.loop.create_task(check_servers())
|
app['dispatch'] = app.loop.create_task(check_servers())
|
||||||
|
|
||||||
|
|
||||||
async def cleanup_background_tasks(app):
|
async def cleanup_background_tasks(app):
|
||||||
app['dispatch'].cancel()
|
app['dispatch'].cancel()
|
||||||
await app['dispatch']
|
await app['dispatch']
|
||||||
|
|
||||||
|
@aiohttp_jinja2.template('index.html')
|
||||||
|
def display_alerts(request):
|
||||||
|
url = str(request.url)
|
||||||
|
if '/tv/' in url:
|
||||||
|
tv_mode = True
|
||||||
|
else:
|
||||||
|
tv_mode = False
|
||||||
|
|
||||||
|
teams = request.match_info.get('teams')
|
||||||
|
try:
|
||||||
|
zapi.logged_in()
|
||||||
|
except Exception as e:
|
||||||
|
_logger.info('[INFO] - {}: Connection to Zabbix API'.format(datetime.datetime.now()))
|
||||||
|
zabbix_login()
|
||||||
|
|
||||||
|
if teams:
|
||||||
|
team_list = teams.lower().split('+')
|
||||||
|
|
||||||
|
check_servers = read_file('./data/zabbix-servers.json')
|
||||||
|
notes = display_notes(request)
|
||||||
|
problems = get_problems(ttl_hash=get_ttl_hash())
|
||||||
|
_logger.info(len(problems))
|
||||||
|
problems = [problem for problem in problems if problem.get('hostgroup').lower() in team_list]
|
||||||
|
problems = sorted(problems,
|
||||||
|
key = lambda i: (i['priority'], i['lastchange']),
|
||||||
|
reverse=True)
|
||||||
|
_logger.info('[NB ALERTS] - {}'.format(len(problems)))
|
||||||
|
context = {'alerts': problems, 'total_alerts': len(problems), 'zabbix_url': settings.ZABBIX_URL, "hostgroups": get_hostgroups(), "notes": notes, "tv_mode": tv_mode, "check_servers": check_servers}
|
||||||
|
return aiohttp_jinja2.render_template(
|
||||||
|
'index.html', request, context)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
app = web.Application()
|
||||||
|
aiohttp_jinja2.setup(app,
|
||||||
|
loader=jinja2.FileSystemLoader('templates'))
|
||||||
|
|
||||||
app = aiohttp.web.Application(logger=_logger)
|
app.add_routes([
|
||||||
|
web.get('/', display_alerts),
|
||||||
app.add_routes([aiohttp.web.get('/', show_alerts),
|
web.get('/{teams}', display_alerts),
|
||||||
|
web.get('/tv/{teams}', display_alerts),
|
||||||
aiohttp.web.post('/post', post_note),
|
aiohttp.web.post('/post', post_note),
|
||||||
aiohttp.web.post('/del', del_note),
|
aiohttp.web.post('/del', del_note),
|
||||||
aiohttp.web.get('/{teams}', show_alerts),
|
|
||||||
aiohttp.web.get('/tv/{teams}', show_alerts),
|
|
||||||
aiohttp.web.static('/images', 'images'),
|
aiohttp.web.static('/images', 'images'),
|
||||||
aiohttp.web.static('/css', 'css'),
|
aiohttp.web.static('/css', 'css'),
|
||||||
aiohttp.web.static('/js', 'js')])
|
aiohttp.web.static('/js', 'js')
|
||||||
|
])
|
||||||
|
|
||||||
app.on_startup.append(start_background_tasks)
|
app.on_startup.append(start_background_tasks)
|
||||||
app.on_cleanup.append(cleanup_background_tasks)
|
app.on_cleanup.append(cleanup_background_tasks)
|
||||||
aiohttp.web.run_app(app, port=PORT)
|
aiohttp.web.run_app(app, port=settings.PORT)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
6
templates/form.html
Normal file
6
templates/form.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<div class='col'><select class='form-control' name='team'>
|
||||||
|
<option value='all'>All teams</option>
|
||||||
|
{% for hostgroup in hostgroups %}
|
||||||
|
<option value="{{ hostgroup }}">{{ hostgroup }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select></div>
|
196
templates/index.html
Normal file
196
templates/index.html
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>[{{total_alerts}}] Super(Vision)</title>
|
||||||
|
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="/css/octicons.min.css">
|
||||||
|
<link rel="stylesheet" href="/css/offline.css">
|
||||||
|
<script src="/js/jquery-3.4.1.slim.min.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/dynafav-1.0.js"></script>
|
||||||
|
<script src="/js/popper.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
|
<script src="/js/offline.min.js" ></script>
|
||||||
|
<style type="text/css">
|
||||||
|
a {
|
||||||
|
color: #DF7401;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #DF7401;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
background-color: #333;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
.disaster {
|
||||||
|
background-color:#D30801;
|
||||||
|
color: #FFFFFF !important;
|
||||||
|
}
|
||||||
|
.disaster a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.table td, .table th { padding: 1px; !important }
|
||||||
|
.image-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 15%;
|
||||||
|
}
|
||||||
|
.btn-group-sm>.btn, .btn-sm {
|
||||||
|
padding: .08rem .5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
|
||||||
|
<a class="navbar-brand" href="/">[{{total_alerts}}] Super(Vision)</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse navbar-sm" id="navbarSupportedContent">
|
||||||
|
<ul class="navbar-nav mr-auto">
|
||||||
|
{% for hostgroup in hostgroups %}
|
||||||
|
<li class='nav-item'><a class='nav-link' href='/{{hostgroup}}'>{{hostgroup}}</a></li>
|
||||||
|
{%endfor%}
|
||||||
|
</ul>
|
||||||
|
<button class="btn btn-outline-secondary" type="button" data-toggle="collapse" data-target="#form-msg" aria-expanded="false" aria-controls="form-message"><span class='octicon octicon-comment'></span></button>
|
||||||
|
</div>
|
||||||
|
</nav> <div class="row-fluid">
|
||||||
|
<div class="container">
|
||||||
|
<div class="collapse" id="form-msg">
|
||||||
|
<div class="card card-body">
|
||||||
|
<form action="/post" method="post" accept-charset="utf-8" enctype="application/x-www-form-urlencoded">
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="col">
|
||||||
|
<input type="text" class="form-control" placeholder="Your name" name="name" required>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<input type="text" class="form-control" placeholder="Message" name="msg" required>
|
||||||
|
<input type="text" class="form-control" name="url" id="url" hidden readonly>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<select class="form-control" name="lvl">
|
||||||
|
<option value="info">Info</option>
|
||||||
|
<option value="warning">Warning</option>
|
||||||
|
<option value="danger">Disaster</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{% include "form.html" %}
|
||||||
|
<div class="col">
|
||||||
|
<button id="publish" name="save" class="btn btn-secondary">Publish <span class="octicon octicon-mail"></span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="container-fluid">
|
||||||
|
{% if check_servers %}
|
||||||
|
<table class='table table-borderless table-sm'>
|
||||||
|
{% for ip in check_servers%}
|
||||||
|
{% if check_servers[ip] != 0 %}
|
||||||
|
<tr class='bg-danger'>
|
||||||
|
<td class='text-center align-left'>
|
||||||
|
<span class='octicon octicon-alert'></span> Zabbix Server: {{ip}} seems unreachable! <span class='octicon octicon-alert'></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{%endif%}
|
||||||
|
{%endfor%}
|
||||||
|
</table>
|
||||||
|
{%endif%}
|
||||||
|
{% if notes %}
|
||||||
|
<table class='table table-borderless'>
|
||||||
|
{% for note in notes %}
|
||||||
|
<tr class='bg-{{note["lvl"]}}'><td class='text-left'><span class='octicon octicon-clock'></span> {{note["date"]}}</td><td class='text-center'> {{note["msg"]}}</td><td class='text-right'><span class='octicon octicon-hubot'></span><i> (by {{note["name"]}})</i></td><td class='text-right'><form action='/del' method='post' accept-charset='utf-8' enctype='application/x-www-form-urlencoded'><input type='text' class='form-control url_note' name='url' hidden readonly><input type='text' name='note_id' value='{{note["ts"]}}' readonly hidden><button type='submit' class='btn btn-outline-light btn-sm' id='del_note' ><span class='octicon octicon-trashcan'></span></button></form></td></tr>"
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
<table class="table table-dark table-sm alerts">
|
||||||
|
{% if alerts|length > 0 %}
|
||||||
|
{% for alert in alerts %}
|
||||||
|
{% if alert["priority"] == '5' %}
|
||||||
|
<tr class="disaster">
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
{%endif%}
|
||||||
|
<td>
|
||||||
|
<span class='badge badge-pill' style='background-color:{{alert["color"]}}'>{{alert["hostgroup"]}}</span>
|
||||||
|
</td>
|
||||||
|
<td>{{alert["host"]}}</td>
|
||||||
|
<td>
|
||||||
|
<a href='{{zabbix_url}}/zabbix.php?action=problem.view&filter_set=1&filter_triggerids%5B%5D={{alert["triggerid"]}}' target='_blank' "+css_class+">{{alert["description"]}}</a>
|
||||||
|
</td>
|
||||||
|
{% if alert["priority"] == '1' %}
|
||||||
|
<td><span class="badge badge-pill badge-info">1</span></td>
|
||||||
|
{% elif alert["priority"] == '2' %}
|
||||||
|
<td><span class="badge badge-pill badge-primary">2</span></td>
|
||||||
|
{% elif alert["priority"] == '3' %}
|
||||||
|
<td><span class="badge badge-pill badge-warning">3</span></td>
|
||||||
|
{% elif alert["priority"] == '4' %}
|
||||||
|
<td><span class="badge badge-pill badge-danger">4</span></td>
|
||||||
|
{% elif alert["priority"] == '5' %}
|
||||||
|
<td><span class="octicon octicon-flame"> </span></td>
|
||||||
|
{% endif %}
|
||||||
|
<td>
|
||||||
|
<span class='badge badge-pill badge-light'>{{alert["since"]}}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{%endfor%}
|
||||||
|
{%else%}
|
||||||
|
<div class="container-fluid image-container"><img src="/images/zabbix_logo_500x131.png" width="30%"/></div>
|
||||||
|
{%endif%}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var refresh;
|
||||||
|
$(document).ready(function() {
|
||||||
|
{% if tv_mode == true %}
|
||||||
|
$('nav').hide();
|
||||||
|
$('form').hide();
|
||||||
|
{% else %}
|
||||||
|
$('nav').show();
|
||||||
|
$('form').show();
|
||||||
|
{% endif %}
|
||||||
|
$('#url').val($(location).attr('href'));
|
||||||
|
$('.url_note').val($(location).attr('href'));
|
||||||
|
$('#del_note').click(function(e)
|
||||||
|
{
|
||||||
|
if(!confirm("This note will be deleted. Are you sure?"))
|
||||||
|
{
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function reload_page() {
|
||||||
|
if(Offline.state != 'up') {
|
||||||
|
clearInterval(refresh);
|
||||||
|
} else {
|
||||||
|
var refresh;
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh = setInterval(reload_page,30000);
|
||||||
|
|
||||||
|
Offline.options = {
|
||||||
|
// to check the connection status immediatly on page load.
|
||||||
|
checkOnLoad: true,
|
||||||
|
// to monitor AJAX requests to check connection.
|
||||||
|
interceptRequests: true,
|
||||||
|
// to automatically retest periodically when the connection is down (set to false to disable).
|
||||||
|
reconnect: {
|
||||||
|
// delay time in seconds to wait before rechecking.
|
||||||
|
initialDelay: 3,
|
||||||
|
// wait time in seconds between retries.
|
||||||
|
delay: 10
|
||||||
|
},
|
||||||
|
// to store and attempt to remake requests which failed while the connection was down.
|
||||||
|
requests: true
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Reference in New Issue
Block a user