tg_monitor_bot/backup/monitor.py.bak_1

218 lines
8.8 KiB
Plaintext
Raw Normal View History

2026-08-02 21:46:32 +00:00
import paramiko
import time
import re
from collections import defaultdict
import logging
logger = logging.getLogger(__name__)
# Глобальный словарь для хранения предыдущих значений RX/TX (для расчета за час)
prev_net_stats = {}
def get_ssh_client(host_config):
"""Создает SSH подключение с детальным логированием"""
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
logger.info(f"🔌 Подключаюсь к {host_config['ip']}:{host_config['port']} как {host_config['user']}")
client.connect(
hostname=host_config['ip'],
port=host_config['port'],
username=host_config['user'],
key_filename=host_config['key_path'],
timeout=10
)
logger.info(f"✅ Подключено к {host_config['ip']}")
return client
except paramiko.AuthenticationException:
error_msg = f"❌ Ошибка аутентификации для {host_config['ip']}! Проверь логин или ключ."
logger.error(error_msg)
return error_msg
except paramiko.SSHException as e:
error_msg = f"❌ SSH ошибка для {host_config['ip']}: {str(e)}"
logger.error(error_msg)
return error_msg
except Exception as e:
error_msg = f"❌ Неизвестная ошибка для {host_config['ip']}: {str(e)}"
logger.error(error_msg)
return error_msg
def exec_cmd(client, command):
"""Выполняет команду и возвращает вывод"""
try:
stdin, stdout, stderr = client.exec_command(command, timeout=10)
return stdout.read().decode('utf-8').strip()
except Exception as e:
return f"CMD Error: {str(e)}"
def collect_metrics(host_config):
"""Сбор всех метрик с одного хоста"""
client = get_ssh_client(host_config)
if isinstance(client, str):
return {"error": client, "host": host_config['name'], "ip": host_config['ip']}
result = {
"host": host_config['name'],
"ip": host_config['ip'],
"metrics": {}
}
# 1. CPU загрузка (%)
cpu_output = exec_cmd(client, "top -bn1 | grep 'Cpu(s)' | awk '{print $2}' | cut -d'%' -f1")
try:
result["metrics"]["cpu"] = float(cpu_output.replace(',', '.'))
except:
result["metrics"]["cpu"] = 0.0
# 2. ОЗУ (используется в MB и %)
mem_output = exec_cmd(client, "free -m | grep Mem")
parts = re.split(r'\s+', mem_output)
if len(parts) >= 3:
total_mem = int(parts[1])
used_mem = int(parts[2])
result["metrics"]["mem_used_mb"] = used_mem
result["metrics"]["mem_percent"] = round((used_mem / total_mem) * 100, 1)
else:
result["metrics"]["mem_used_mb"] = 0
result["metrics"]["mem_percent"] = 0
# 3. Uptime и Load Average
uptime_output = exec_cmd(client, "uptime")
load_avg = re.search(r'load average:\s+([\d.]+),\s+([\d.]+),\s+([\d.]+)', uptime_output)
if load_avg:
result["metrics"]["load_1"] = load_avg.group(1)
result["metrics"]["load_5"] = load_avg.group(2)
result["metrics"]["load_15"] = load_avg.group(3)
uptime_seconds = exec_cmd(client, "cat /proc/uptime | awk '{print $1}'")
try:
uptime_seconds = float(uptime_seconds.split('.')[0])
days = uptime_seconds // 86400
hours = (uptime_seconds % 86400) // 3600
minutes = (uptime_seconds % 3600) // 60
result["metrics"]["uptime"] = f"{int(days)}d {int(hours)}h {int(minutes)}m"
except:
result["metrics"]["uptime"] = "N/A"
# 4. Сеть (трафик за последний час)
net_output = exec_cmd(client, "cat /proc/net/dev | tail -n +3 | grep -v lo")
current_net = {}
for line in net_output.split('\n'):
if not line.strip():
continue
parts = re.split(r'\s+', line.strip())
iface = parts[0].replace(':', '')
rx_bytes = int(parts[1])
tx_bytes = int(parts[9])
current_net[iface] = {'rx': rx_bytes, 'tx': tx_bytes}
global prev_net_stats
host_key = host_config['ip']
total_rx_hour = 0
total_tx_hour = 0
if host_key in prev_net_stats:
old_data = prev_net_stats[host_key]
for iface, vals in current_net.items():
if iface in old_data:
delta_rx = vals['rx'] - old_data[iface]['rx']
delta_tx = vals['tx'] - old_data[iface]['tx']
if delta_rx > 0:
total_rx_hour += delta_rx
if delta_tx > 0:
total_tx_hour += delta_tx
result["metrics"]["net_rx_mb"] = round(total_rx_hour / (1024 * 1024), 2)
result["metrics"]["net_tx_mb"] = round(total_tx_hour / (1024 * 1024), 2)
prev_net_stats[host_key] = current_net
# 5. Диски (iostat %util)
iostat_output = exec_cmd(client, "iostat -x -k 1 2 | grep -E 'sd[a-z]|vd[a-z]|nvme' | tail -n +3")
disk_stats = []
for line in iostat_output.split('\n'):
if not line.strip():
continue
parts = re.split(r'\s+', line.strip())
if len(parts) >= 14:
disk_name = parts[0]
try:
util = float(parts[-1].replace(',', '.'))
rps = float(parts[3].replace(',', '.'))
wps = float(parts[4].replace(',', '.'))
disk_stats.append({
"name": disk_name,
"util": util,
"rps": rps,
"wps": wps
})
except:
pass
disk_stats.sort(key=lambda x: x['util'], reverse=True)
result["metrics"]["disks"] = disk_stats[:3]
# 6. Температуры
temp_output = exec_cmd(client, "sensors -u 2>/dev/null | grep -E 'temp[0-9]+_input|Core' | head -5")
temps = []
if temp_output and "Error" not in temp_output:
for line in temp_output.split('\n'):
if '_input:' in line:
val = re.search(r'([\d.]+)', line)
if val:
temps.append(float(val.group(1)))
result["metrics"]["temps"] = temps
# 7. Топ-5 процессов по CPU и RAM
top_cpu = exec_cmd(client, "ps aux --sort=-%cpu | head -6 | tail -5")
top_mem = exec_cmd(client, "ps aux --sort=-%mem | head -6 | tail -5")
result["metrics"]["top_cpu"] = top_cpu
result["metrics"]["top_mem"] = top_mem
# 8. Логи (ошибки)
last_logs = exec_cmd(client, "journalctl -p 3 -xn 5 --no-pager 2>/dev/null || dmesg | tail -5")
result["metrics"]["logs_errors"] = last_logs
# 9. Fail2ban (УЛУЧШЕННАЯ ВЕРСИЯ)
# Проверяем статус
fail2ban_status = exec_cmd(client, "fail2ban-client status 2>/dev/null")
if not fail2ban_status or "not found" in fail2ban_status:
# Проверяем, запущен ли процесс
ps_fail2ban = exec_cmd(client, "ps aux | grep -v grep | grep fail2ban-server")
if ps_fail2ban:
fail2ban_status = "✅ Fail2ban запущен (проверь джейлы командой fail2ban-client status)"
else:
fail2ban_status = "❌ Fail2ban не установлен или не запущен"
else:
# Парсим статус для красивого вывода
lines = fail2ban_status.split('\n')
jails_info = []
for line in lines:
if '|- Number of jail:' in line or '`- Number of jail:' in line:
jails_info.append(line.strip())
if 'Currently banned:' in line:
jails_info.append(line.strip())
if jails_info:
fail2ban_status = "✅ Fail2ban активен\n" + "\n".join(jails_info)
# Логи банов
fail2ban_logs = exec_cmd(client, "tail -20 /var/log/fail2ban.log 2>/dev/null | grep -E 'BAN|UNBAN' | tail -5 || echo 'Нет записей о банах'")
# Активные баны
active_bans = exec_cmd(client, "fail2ban-client status 2>/dev/null | grep -A 5 'Currently banned:'")
if active_bans and "Currently banned:" in active_bans:
fail2ban_logs += "\n\n🔒 Активные баны:\n" + active_bans
result["metrics"]["fail2ban_status"] = fail2ban_status
result["metrics"]["fail2ban_logs"] = fail2ban_logs
# 10. Проверка на OOM
oom_check = exec_cmd(client, "dmesg | grep -i 'out of memory' | tail -3 || echo 'Нет OOM-событий'")
if oom_check and "Нет OOM-событий" not in oom_check:
result["metrics"]["oom"] = oom_check
else:
result["metrics"]["oom"] = "✅ Нет OOM-событий"
client.close()
return result