#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ LogBoard+ - Docker функции Автор: Сергей Антропов Сайт: https://devops.org.ru """ import json import os from typing import List, Dict, Optional import docker from core.config import ( DEFAULT_TAIL, DEFAULT_PROJECT, DEFAULT_PROJECTS, SKIP_UNHEALTHY ) from core.logger import docker_logger # Инициализация Docker клиента docker_client = docker.from_env() def load_excluded_containers() -> List[str]: """ Загружает список исключенных контейнеров из JSON файла Автор: Сергей Антропов Сайт: https://devops.org.ru """ try: with open("app/excluded_containers.json", "r", encoding="utf-8") as f: data = json.load(f) return data.get("excluded_containers", []) except FileNotFoundError: docker_logger.warning("Файл app/excluded_containers.json не найден, используем пустой список") return [] except json.JSONDecodeError as e: docker_logger.error(f"Ошибка парсинга app/excluded_containers.json: {e}") return [] except Exception as e: docker_logger.error(f"Ошибка загрузки app/excluded_containers.json: {e}") return [] def save_excluded_containers(containers: List[str]) -> bool: """ Сохраняет список исключенных контейнеров в JSON файл Автор: Сергей Антропов Сайт: https://devops.org.ru """ try: data = { "excluded_containers": containers, "description": "Список контейнеров, которые генерируют слишком много логов и исключаются из отображения" } with open("app/excluded_containers.json", "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) return True except Exception as e: docker_logger.error(f"Ошибка сохранения app/excluded_containers.json: {e}") return False def get_all_projects() -> List[str]: """ Получает список всех проектов Docker Compose с учетом исключенных контейнеров Автор: Сергей Антропов Сайт: https://devops.org.ru """ projects = set() excluded_containers = load_excluded_containers() try: containers = docker_client.containers.list(all=True) # Словарь для подсчета контейнеров по проектам project_containers = {} standalone_containers = [] for c in containers: try: # Пропускаем исключенные контейнеры if c.name in excluded_containers: continue labels = c.labels or {} project = labels.get("com.docker.compose.project") if project: if project not in project_containers: project_containers[project] = 0 project_containers[project] += 1 else: standalone_containers.append(c.name) except Exception: continue # Добавляем проекты, у которых есть хотя бы один неисключенный контейнер for project, count in project_containers.items(): if count > 0: projects.add(project) # Добавляем standalone, если есть неисключенные контейнеры без проекта if standalone_containers: projects.add("standalone") except Exception as e: docker_logger.error(f"Ошибка получения списка проектов: {e}") return [] result = sorted(list(projects)) docker_logger.info(f"Доступные проекты (с учетом исключенных контейнеров): {result}") return result def list_containers(projects: Optional[List[str]] = None, include_stopped: bool = False) -> List[Dict]: """ Получает список контейнеров с поддержкой множественных проектов Автор: Сергей Антропов Сайт: https://devops.org.ru """ # Загружаем список исключенных контейнеров из JSON файла excluded_containers = load_excluded_containers() docker_logger.info(f"Список исключенных контейнеров: {excluded_containers}") items = [] excluded_count = 0 try: # Получаем список контейнеров с базовой обработкой ошибок containers = docker_client.containers.list(all=include_stopped) for c in containers: try: # Базовая информация о контейнере (без health check) basic_info = { "id": c.id[:12], "name": c.name, "status": c.status, "image": "unknown", "service": c.name, "project": None, "health": None, "ports": [], "url": None, } # Безопасно получаем метки try: labels = c.labels or {} basic_info["project"] = labels.get("com.docker.compose.project") basic_info["service"] = labels.get("com.docker.compose.service") or c.name except Exception: pass # Используем значения по умолчанию # Безопасно получаем информацию об образе try: if c.image and c.image.tags: basic_info["image"] = c.image.tags[0] elif c.image: basic_info["image"] = c.image.short_id except Exception: pass # Оставляем "unknown" # Безопасно получаем информацию о портах try: ports = c.ports or {} if ports: basic_info["ports"] = list(ports.keys()) # Пытаемся найти HTTP/HTTPS порт для создания URL for port_mapping in ports.values(): if port_mapping: for mapping in port_mapping: if isinstance(mapping, dict) and mapping.get("HostPort"): host_port = mapping["HostPort"] # Проверяем, что это HTTP порт (80, 443, 8080, 3000, etc.) host_port_int = int(host_port) if (host_port_int in [80, 443] or (1 <= host_port_int <= 7999) or (8000 <= host_port_int <= 65535)): protocol = "https" if host_port == "443" else "http" basic_info["url"] = f"{protocol}://localhost:{host_port}" basic_info["host_port"] = host_port break if basic_info["url"]: break except Exception: pass # Оставляем пустые значения # Фильтрация по проектам if projects: # Если проект не указан, считаем его standalone container_project = basic_info["project"] or "standalone" if container_project not in projects: continue # Фильтрация исключенных контейнеров if basic_info["name"] in excluded_containers: excluded_count += 1 docker_logger.warning(f"Пропускаем исключенный контейнер: {basic_info['name']}") continue # Добавляем контейнер в список items.append(basic_info) except Exception as e: # Пропускаем контейнеры с критическими ошибками docker_logger.warning(f"Пропускаем проблемный контейнер {c.name if hasattr(c, 'name') else 'unknown'} (ID: {c.id[:12]}): {e}") continue except Exception as e: docker_logger.error(f"Ошибка получения списка контейнеров: {e}") return [] # Сортируем по проекту, сервису и имени items.sort(key=lambda x: (x.get("project") or "", x.get("service") or "", x.get("name") or "")) # Подсчитываем статистику по проектам project_stats = {} for item in items: project = item.get("project") or "standalone" if project not in project_stats: project_stats[project] = {"visible": 0, "excluded": 0} project_stats[project]["visible"] += 1 # Подсчитываем исключенные контейнеры по проектам for c in containers: try: if c.name in excluded_containers: labels = c.labels or {} project = labels.get("com.docker.compose.project") or "standalone" if project not in project_stats: project_stats[project] = {"visible": 0, "excluded": 0} project_stats[project]["excluded"] += 1 except Exception: continue docker_logger.info(f"Статистика: найдено {len(items)} контейнеров, исключено {excluded_count} контейнеров") for project, stats in project_stats.items(): docker_logger.info(f" 📦 {project}: {stats['visible']} видимых, {stats['excluded']} исключенных") return items