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