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__ = [
 | 
			
		||||
    "conf",
 | 
			
		||||
    STATIC_DIR,
 | 
			
		||||
    icon_dict,
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,15 @@ import logging
 | 
			
		|||
BASE_DIR = Path(__file__).parent.parent
 | 
			
		||||
TEMPLATES_DIR = BASE_DIR / "web" / "templates"
 | 
			
		||||
STATIC_DIR = BASE_DIR / "web" / "static"
 | 
			
		||||
icon_dict = {
 | 
			
		||||
    "0": "",
 | 
			
		||||
    "1": "🟦",
 | 
			
		||||
    "2": "🟨",
 | 
			
		||||
    "3": "🟧",
 | 
			
		||||
    "4": "🟥",
 | 
			
		||||
    "5": "🟫",
 | 
			
		||||
    "10": "✅",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
from zabbix import get_active_problems
 | 
			
		||||
from config import conf
 | 
			
		||||
from config import conf, icon_dict
 | 
			
		||||
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()
 | 
			
		||||
    for i in active_alerts:
 | 
			
		||||
        print(i)
 | 
			
		||||
    print(len(active_alerts))
 | 
			
		||||
    telegram_alerts = await get_all_keys()
 | 
			
		||||
 | 
			
		||||
    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__":
 | 
			
		||||
    log.info("Starting app")
 | 
			
		||||
    try:
 | 
			
		||||
        while True:
 | 
			
		||||
            main_loop()
 | 
			
		||||
            asyncio.run(main_loop())
 | 
			
		||||
            sleep(conf.zabbix.upd_interval)
 | 
			
		||||
    except KeyboardInterrupt:
 | 
			
		||||
        log.info("Manual app stopped")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,9 @@ from .crud import (
 | 
			
		|||
    set_value,
 | 
			
		||||
    ping,
 | 
			
		||||
    pop_value,
 | 
			
		||||
    get_all,
 | 
			
		||||
    get_all_keys,
 | 
			
		||||
    del_value,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
__all__ = [
 | 
			
		||||
| 
						 | 
				
			
			@ -10,4 +13,7 @@ __all__ = [
 | 
			
		|||
    "set_value",
 | 
			
		||||
    "ping",
 | 
			
		||||
    "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)
 | 
			
		||||
            log.info("Get and delete %s = %s", key, 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 = {
 | 
			
		||||
            "host": conf.redis.host,
 | 
			
		||||
            "port": conf.redis.port,
 | 
			
		||||
            "decode_responses": True,
 | 
			
		||||
        }
 | 
			
		||||
        if 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(
 | 
			
		||||
    message: str,
 | 
			
		||||
 | 
			
		||||
) -> dict | None:
 | 
			
		||||
) -> dict:
 | 
			
		||||
    url = f"https://api.telegram.org/bot{conf.tgbot.token}/sendMessage"
 | 
			
		||||
    params = {
 | 
			
		||||
        "chat_id": conf.tgbot.chat_id,
 | 
			
		||||
| 
						 | 
				
			
			@ -16,37 +15,52 @@ async def send_message(
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    async with aiohttp.ClientSession() as session:
 | 
			
		||||
        async with session.post(
 | 
			
		||||
            url,
 | 
			
		||||
            json=params,
 | 
			
		||||
        ) as response:
 | 
			
		||||
            log.info(f"Response status: {response.status}")
 | 
			
		||||
            resp = await response.json()
 | 
			
		||||
            if response.status == 200:
 | 
			
		||||
                log.info(f"Message with ID: {resp['result']['message_id']} send")
 | 
			
		||||
                return {
 | 
			
		||||
                    "status": response.status,
 | 
			
		||||
                    "msg_id": resp["result"]["message_id"],
 | 
			
		||||
                }
 | 
			
		||||
            log.warning(f"Message not send. Response status: {response.status}")
 | 
			
		||||
        try:
 | 
			
		||||
            async with session.post(
 | 
			
		||||
                url,
 | 
			
		||||
                json=params,
 | 
			
		||||
            ) as response:
 | 
			
		||||
                log.info(f"Response status: {response.status}")
 | 
			
		||||
                resp = await response.json()
 | 
			
		||||
                if response.status == 200:
 | 
			
		||||
                    log.info(f"Message with ID: {resp['result']['message_id']} send")
 | 
			
		||||
                    return {
 | 
			
		||||
                        "status": response.status,
 | 
			
		||||
                        "msg_id": resp["result"]["message_id"],
 | 
			
		||||
                    }
 | 
			
		||||
                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(
 | 
			
		||||
    message_id: int,
 | 
			
		||||
) -> dict | None:
 | 
			
		||||
) -> dict:
 | 
			
		||||
    url = f"https://api.telegram.org/bot{conf.tgbot.token}/deleteMessage"
 | 
			
		||||
    async with aiohttp.ClientSession() as session:
 | 
			
		||||
        async with session.post(
 | 
			
		||||
            url,
 | 
			
		||||
            json={
 | 
			
		||||
                "chat_id": conf.tgbot.chat_id,
 | 
			
		||||
                "message_id": message_id,
 | 
			
		||||
            },
 | 
			
		||||
        ) as response:
 | 
			
		||||
            if response.status == 200:
 | 
			
		||||
                log.info(f"Message ID {message_id} deleted")
 | 
			
		||||
                return {
 | 
			
		||||
                    "status": response.status,
 | 
			
		||||
                }
 | 
			
		||||
            else:
 | 
			
		||||
                log.warning(f"Response status: {response.status}")
 | 
			
		||||
        try:
 | 
			
		||||
            async with session.post(
 | 
			
		||||
                url,
 | 
			
		||||
                json={
 | 
			
		||||
                    "chat_id": conf.tgbot.chat_id,
 | 
			
		||||
                    "message_id": message_id,
 | 
			
		||||
                },
 | 
			
		||||
            ) as response:
 | 
			
		||||
                if response.status == 200:
 | 
			
		||||
                    log.info(f"Message ID {message_id} deleted")
 | 
			
		||||
                    return {
 | 
			
		||||
                        "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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_active_problems():
 | 
			
		||||
def get_active_problems() -> dict:
 | 
			
		||||
    api = ZabbixAPI(url=conf.zabbix.url, token=conf.zabbix.token)
 | 
			
		||||
    try:
 | 
			
		||||
        problems = api.problem.get(
 | 
			
		||||
| 
						 | 
				
			
			@ -32,8 +32,11 @@ def get_active_problems():
 | 
			
		|||
                "severity",
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
        events_dict = {"event_ids": []}
 | 
			
		||||
        for event in events:
 | 
			
		||||
            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:
 | 
			
		||||
        log.warning("Get event from zabbix error")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue