""" Сервис для развертывания ролей на реальные серверы Автор: Сергей Антропов Сайт: https://devops.org.ru """ import asyncio from pathlib import Path from typing import Optional, AsyncGenerator, Dict, List from datetime import datetime from app.core.config import settings from app.core.ansible_executor import AnsibleExecutor class DeploymentService: """Развертывание ролей на реальные серверы""" def __init__(self): self.project_root = settings.PROJECT_ROOT self.inventory_file = self.project_root / "inventory" / "hosts.ini" self.deploy_playbook = self.project_root / "roles" / "deploy.yml" self.ansible_executor = AnsibleExecutor() async def deploy_role( self, role_name: str, inventory: Optional[str] = None, limit: Optional[str] = None, tags: Optional[List[str]] = None, check: bool = False, extra_vars: Optional[Dict] = None, stream: bool = False ) -> AsyncGenerator[str, None]: """ Развертывание роли на реальные серверы Args: role_name: Имя роли для развертывания inventory: Путь к inventory файлу (по умолчанию inventory/hosts.ini) limit: Ограничение на хосты tags: Список тегов для фильтрации check: Режим dry-run (--check) extra_vars: Дополнительные переменные stream: Если True, возвращает генератор строк Yields: Строки вывода команды """ # Проверка существования роли role_path = self.project_root / "roles" / role_name if not role_path.exists(): yield f"❌ Роль '{role_name}' не найдена\n" return # Определение inventory файла if inventory: inventory_path = Path(inventory) if not inventory_path.is_absolute(): inventory_path = self.project_root / inventory_path else: inventory_path = self.inventory_file if not inventory_path.exists(): yield f"❌ Inventory файл '{inventory_path}' не найден\n" yield "💡 Создайте файл inventory/hosts.ini с вашими серверами\n" return # Проверка deploy.yml if not self.deploy_playbook.exists(): yield f"❌ Playbook '{self.deploy_playbook}' не найден\n" return # Формирование тегов deploy_tags = [role_name] if tags: deploy_tags.extend(tags) yield f"🚀 Развертывание роли '{role_name}' на реальные серверы...\n" if check: yield "⚠️ Режим dry-run (--check) - изменения не будут применены\n" yield f"📋 Inventory: {inventory_path}\n" if limit: yield f"📋 Limit: {limit}\n" yield f"📋 Tags: {', '.join(deploy_tags)}\n\n" # Запуск ansible-playbook async for line in self.ansible_executor.run_playbook( playbook_path=str(self.deploy_playbook), inventory=str(inventory_path), tags=deploy_tags, limit=limit, check=check, extra_vars=extra_vars, stream=True ): yield line yield "\n✅ Развертывание завершено\n" async def dry_run_role( self, role_name: str, inventory: Optional[str] = None, limit: Optional[str] = None, stream: bool = False ) -> AsyncGenerator[str, None]: """ Dry-run проверка роли на реальных серверах (без изменений) Args: role_name: Имя роли inventory: Путь к inventory файлу limit: Ограничение на хосты stream: Если True, возвращает генератор строк Yields: Строки вывода команды """ yield f"🔍 Dry-run проверка роли '{role_name}' на реальных серверах...\n" yield "⚠️ Безопасно: не изменяет серверы, только проверяет\n\n" async for line in self.deploy_role( role_name=role_name, inventory=inventory, limit=limit, check=True, stream=True ): yield line def detect_log_level(self, line: str) -> str: """Определение уровня лога из строки""" return self.ansible_executor.detect_log_level(line)