12.03
This commit is contained in:
parent
fc1e34e727
commit
273b972b49
|
@ -1,7 +1,8 @@
|
||||||
from .config import conf, STATIC_DIR
|
from .config import conf, STATIC_DIR, icon_dict
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"conf",
|
"conf",
|
||||||
STATIC_DIR,
|
STATIC_DIR,
|
||||||
|
icon_dict,
|
||||||
]
|
]
|
||||||
|
|
|
@ -12,6 +12,15 @@ import logging
|
||||||
BASE_DIR = Path(__file__).parent.parent
|
BASE_DIR = Path(__file__).parent.parent
|
||||||
TEMPLATES_DIR = BASE_DIR / "web" / "templates"
|
TEMPLATES_DIR = BASE_DIR / "web" / "templates"
|
||||||
STATIC_DIR = BASE_DIR / "web" / "static"
|
STATIC_DIR = BASE_DIR / "web" / "static"
|
||||||
|
icon_dict = {
|
||||||
|
"0": "",
|
||||||
|
"1": "🟦",
|
||||||
|
"2": "🟨",
|
||||||
|
"3": "🟧",
|
||||||
|
"4": "🟥",
|
||||||
|
"5": "🟫",
|
||||||
|
"10": "✅",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class LogConfig(BaseModel):
|
class LogConfig(BaseModel):
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
FROM python:3.12-slim
|
||||||
|
WORKDIR /app
|
||||||
|
ADD config /app/config
|
||||||
|
ADD redis_db /app/redis_db
|
||||||
|
ADD telegram /app/telegram
|
||||||
|
ADD zabbix /app/zabbix
|
||||||
|
ADD main.py /app/main.py
|
||||||
|
ADD pyproject.toml /app/pyproject.toml
|
||||||
|
ADD uv.lock /app/uv.lock
|
||||||
|
RUN pip install --upgrade pip
|
||||||
|
RUN pip install uv
|
||||||
|
RUN uv sync --no-dev
|
||||||
|
CMD ["uv", "run", "main.py"]
|
|
@ -0,0 +1,34 @@
|
||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:latest
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./local_redis_file/data:/data
|
||||||
|
command: [redis-server, --protected-mode yes, --port 6379, --requirepass, P@ssw0rd!]
|
||||||
|
|
||||||
|
tg-bot:
|
||||||
|
image: git.sm8255082.ru/osnova/zbx-tg-bot:1.0.0
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
environment:
|
||||||
|
- OAA_CFG__LOG__LEVEL=30
|
||||||
|
- OAA_CFG__LOG__LEVEL_TO_FILE=30
|
||||||
|
|
||||||
|
- CFG__ZABBIX__URL=https://zabbix.example.com
|
||||||
|
- CFG__ZABBIX__TOKEN=string
|
||||||
|
- CFG__ZABBIX__MIN_SEVERITY=2
|
||||||
|
- CFG__ZABBIX__UPD_INTERVAL=30
|
||||||
|
|
||||||
|
- CFG__TGBOT__TOKEN=string
|
||||||
|
- CFG__TGBOT__CHAT_ID=000000
|
||||||
|
- CFG__TGBOT__TREAD_ID=0
|
||||||
|
|
||||||
|
- CFG__REDIS__HOST=redis
|
||||||
|
- CFG__REDIS__PORT=6379
|
||||||
|
- CFG__REDIS__PWD=P@ssw0rd!
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:latest
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
volumes:
|
||||||
|
- ./local_redis_file/data:/data
|
||||||
|
#command: ["redis-server", --port 6379]
|
||||||
|
command: [redis-server, --protected-mode yes, --port 6379, --requirepass, P@ssw0rd!]
|
52
main.py
52
main.py
|
@ -1,21 +1,61 @@
|
||||||
import logging as log
|
import logging as log
|
||||||
from zabbix import get_active_problems
|
from zabbix import get_active_problems
|
||||||
from config import conf
|
from config import conf, icon_dict
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
from redis_db import (
|
||||||
|
get_all_keys,
|
||||||
|
get_value,
|
||||||
|
set_value,
|
||||||
|
del_value,
|
||||||
|
)
|
||||||
|
import asyncio
|
||||||
|
from telegram import del_message, send_message
|
||||||
|
|
||||||
|
|
||||||
def main_loop():
|
async def main_loop():
|
||||||
active_alerts = get_active_problems()
|
active_alerts = get_active_problems()
|
||||||
for i in active_alerts:
|
telegram_alerts = await get_all_keys()
|
||||||
print(i)
|
|
||||||
print(len(active_alerts))
|
new_alerts_id = []
|
||||||
|
closed_alerts_id = []
|
||||||
|
|
||||||
|
for active_alert in active_alerts["event_ids"]:
|
||||||
|
if active_alert not in telegram_alerts:
|
||||||
|
new_alerts_id.append(active_alert)
|
||||||
|
|
||||||
|
for telegram_alert in telegram_alerts:
|
||||||
|
if telegram_alert not in active_alerts["event_ids"]:
|
||||||
|
closed_alerts_id.append(telegram_alert)
|
||||||
|
|
||||||
|
log.info("new " + str(new_alerts_id))
|
||||||
|
log.info("closed " + str(closed_alerts_id))
|
||||||
|
|
||||||
|
for new_alert in new_alerts_id:
|
||||||
|
message = (
|
||||||
|
icon_dict[active_alerts[new_alert]["severity"]]
|
||||||
|
+ f"{active_alerts[new_alert]['host']}\n"
|
||||||
|
+ f"{active_alerts[new_alert]['name']}"
|
||||||
|
)
|
||||||
|
msg_id = await send_message(message)
|
||||||
|
if msg_id["status"] == 200:
|
||||||
|
await set_value(key=new_alert, value=msg_id["msg_id"])
|
||||||
|
|
||||||
|
for closed_alert in closed_alerts_id:
|
||||||
|
msg_id = await get_value(closed_alert)
|
||||||
|
if msg_id:
|
||||||
|
resp = await del_message(int(msg_id))
|
||||||
|
if resp["status"] == 200:
|
||||||
|
await del_value(closed_alert)
|
||||||
|
if resp["status"] == 400:
|
||||||
|
log.warning(f"remove olg message {msg_id} from reddis")
|
||||||
|
await del_value(closed_alert)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
log.info("Starting app")
|
log.info("Starting app")
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
main_loop()
|
asyncio.run(main_loop())
|
||||||
sleep(conf.zabbix.upd_interval)
|
sleep(conf.zabbix.upd_interval)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
log.info("Manual app stopped")
|
log.info("Manual app stopped")
|
||||||
|
|
|
@ -3,6 +3,9 @@ from .crud import (
|
||||||
set_value,
|
set_value,
|
||||||
ping,
|
ping,
|
||||||
pop_value,
|
pop_value,
|
||||||
|
get_all,
|
||||||
|
get_all_keys,
|
||||||
|
del_value,
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -10,4 +13,7 @@ __all__ = [
|
||||||
"set_value",
|
"set_value",
|
||||||
"ping",
|
"ping",
|
||||||
"pop_value",
|
"pop_value",
|
||||||
|
"get_all",
|
||||||
|
"get_all_keys",
|
||||||
|
"del_value",
|
||||||
]
|
]
|
||||||
|
|
|
@ -31,3 +31,29 @@ async def pop_value(key):
|
||||||
value = await redis_connect.client.getdel(key)
|
value = await redis_connect.client.getdel(key)
|
||||||
log.info("Get and delete %s = %s", key, value)
|
log.info("Get and delete %s = %s", key, value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
async def del_value(key):
|
||||||
|
async with RedisManager() as redis_connect:
|
||||||
|
if redis_connect:
|
||||||
|
value = await redis_connect.client.delete(key)
|
||||||
|
log.info("Delete %s = %s", key, value)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_all():
|
||||||
|
async with RedisManager() as redis_connect:
|
||||||
|
if redis_connect:
|
||||||
|
all_data = {}
|
||||||
|
async for key in redis_connect.client.scan_iter():
|
||||||
|
value = await redis_connect.client.get(key)
|
||||||
|
all_data[key] = value if value else None
|
||||||
|
return all_data
|
||||||
|
|
||||||
|
|
||||||
|
async def get_all_keys():
|
||||||
|
async with RedisManager() as redis_connect:
|
||||||
|
if redis_connect:
|
||||||
|
all_keys = []
|
||||||
|
async for key in redis_connect.client.scan_iter():
|
||||||
|
all_keys.append(key)
|
||||||
|
return all_keys
|
||||||
|
|
|
@ -10,6 +10,7 @@ class RedisManager:
|
||||||
self.connect_params = {
|
self.connect_params = {
|
||||||
"host": conf.redis.host,
|
"host": conf.redis.host,
|
||||||
"port": conf.redis.port,
|
"port": conf.redis.port,
|
||||||
|
"decode_responses": True,
|
||||||
}
|
}
|
||||||
if conf.redis.pwd:
|
if conf.redis.pwd:
|
||||||
self.connect_params["password"] = conf.redis.pwd
|
self.connect_params["password"] = conf.redis.pwd
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
from .message import (
|
||||||
|
send_message,
|
||||||
|
del_message,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"send_message",
|
||||||
|
"del_message",
|
||||||
|
]
|
|
@ -6,8 +6,7 @@ from config import conf
|
||||||
|
|
||||||
async def send_message(
|
async def send_message(
|
||||||
message: str,
|
message: str,
|
||||||
|
) -> dict:
|
||||||
) -> dict | None:
|
|
||||||
url = f"https://api.telegram.org/bot{conf.tgbot.token}/sendMessage"
|
url = f"https://api.telegram.org/bot{conf.tgbot.token}/sendMessage"
|
||||||
params = {
|
params = {
|
||||||
"chat_id": conf.tgbot.chat_id,
|
"chat_id": conf.tgbot.chat_id,
|
||||||
|
@ -16,37 +15,52 @@ async def send_message(
|
||||||
}
|
}
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.post(
|
try:
|
||||||
url,
|
async with session.post(
|
||||||
json=params,
|
url,
|
||||||
) as response:
|
json=params,
|
||||||
log.info(f"Response status: {response.status}")
|
) as response:
|
||||||
resp = await response.json()
|
log.info(f"Response status: {response.status}")
|
||||||
if response.status == 200:
|
resp = await response.json()
|
||||||
log.info(f"Message with ID: {resp['result']['message_id']} send")
|
if response.status == 200:
|
||||||
return {
|
log.info(f"Message with ID: {resp['result']['message_id']} send")
|
||||||
"status": response.status,
|
return {
|
||||||
"msg_id": resp["result"]["message_id"],
|
"status": response.status,
|
||||||
}
|
"msg_id": resp["result"]["message_id"],
|
||||||
log.warning(f"Message not send. Response status: {response.status}")
|
}
|
||||||
|
else:
|
||||||
|
log.warning(f"Message not send. Response status: {response.status}")
|
||||||
|
return {"status": response.status}
|
||||||
|
except Exception as e:
|
||||||
|
log.warning(f"Exception: {e}")
|
||||||
|
return {"status": e}
|
||||||
|
|
||||||
|
|
||||||
async def del_message(
|
async def del_message(
|
||||||
message_id: int,
|
message_id: int,
|
||||||
) -> dict | None:
|
) -> dict:
|
||||||
url = f"https://api.telegram.org/bot{conf.tgbot.token}/deleteMessage"
|
url = f"https://api.telegram.org/bot{conf.tgbot.token}/deleteMessage"
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.post(
|
try:
|
||||||
url,
|
async with session.post(
|
||||||
json={
|
url,
|
||||||
"chat_id": conf.tgbot.chat_id,
|
json={
|
||||||
"message_id": message_id,
|
"chat_id": conf.tgbot.chat_id,
|
||||||
},
|
"message_id": message_id,
|
||||||
) as response:
|
},
|
||||||
if response.status == 200:
|
) as response:
|
||||||
log.info(f"Message ID {message_id} deleted")
|
if response.status == 200:
|
||||||
return {
|
log.info(f"Message ID {message_id} deleted")
|
||||||
"status": response.status,
|
return {
|
||||||
}
|
"status": response.status,
|
||||||
else:
|
}
|
||||||
log.warning(f"Response status: {response.status}")
|
else:
|
||||||
|
log.warning(
|
||||||
|
f"Message ID {message_id} NOT deleted. Response status: {response.status}"
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"status": response.status,
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
log.warning(f"Exception: {e}")
|
||||||
|
return {"status": e}
|
||||||
|
|
|
@ -5,7 +5,7 @@ from zabbix_utils import ZabbixAPI
|
||||||
from config import conf
|
from config import conf
|
||||||
|
|
||||||
|
|
||||||
def get_active_problems():
|
def get_active_problems() -> dict:
|
||||||
api = ZabbixAPI(url=conf.zabbix.url, token=conf.zabbix.token)
|
api = ZabbixAPI(url=conf.zabbix.url, token=conf.zabbix.token)
|
||||||
try:
|
try:
|
||||||
problems = api.problem.get(
|
problems = api.problem.get(
|
||||||
|
@ -32,8 +32,11 @@ def get_active_problems():
|
||||||
"severity",
|
"severity",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
events_dict = {"event_ids": []}
|
||||||
for event in events:
|
for event in events:
|
||||||
event["host"] = event.pop("hosts", None)[0]["host"]
|
event["host"] = event.pop("hosts", None)[0]["host"]
|
||||||
return events
|
events_dict[event["eventid"]] = event
|
||||||
|
events_dict["event_ids"].append(event["eventid"])
|
||||||
|
return events_dict
|
||||||
except:
|
except:
|
||||||
log.warning("Get event from zabbix error")
|
log.warning("Get event from zabbix error")
|
||||||
|
|
Loading…
Reference in New Issue