""" Модели базы данных для истории команд и тестов Автор: Сергей Антропов Сайт: https://devops.org.ru """ from sqlalchemy import Column, Integer, String, DateTime, Text, Boolean, JSON, ForeignKey from sqlalchemy.orm import relationship from datetime import datetime # Импортируем Base из user.py для единообразия from app.models.user import Base class CommandHistory(Base): """История выполнения команд""" __tablename__ = "command_history" id = Column(Integer, primary_key=True, index=True) command = Column(String, nullable=False, index=True) command_type = Column(String, nullable=False, index=True) # test, deploy, export, import role_name = Column(String, index=True) preset_name = Column(String) status = Column(String, nullable=False) # success, failed, running stdout = Column(Text) stderr = Column(Text) returncode = Column(Integer) started_at = Column(DateTime, default=datetime.utcnow, nullable=False) finished_at = Column(DateTime) duration = Column(Integer) # в секундах user = Column(String) extra_data = Column(JSON) # Дополнительные данные # Связи test_results = relationship("TestResult", back_populates="command") class TestResult(Base): """Результаты тестирования ролей""" __tablename__ = "test_results" id = Column(Integer, primary_key=True, index=True) command_id = Column(Integer, ForeignKey("command_history.id"), nullable=False) role_name = Column(String, nullable=False, index=True) preset_name = Column(String, index=True) test_type = Column(String) # molecule, lint, syntax status = Column(String, nullable=False) # passed, failed, skipped duration = Column(Integer) # в секундах output = Column(Text) error = Column(Text) created_at = Column(DateTime, default=datetime.utcnow, nullable=False) # Связи command = relationship("CommandHistory", back_populates="test_results") class DeploymentHistory(Base): """История деплоев""" __tablename__ = "deployment_history" id = Column(Integer, primary_key=True, index=True) role_name = Column(String, nullable=False, index=True) inventory = Column(String) hosts = Column(JSON) # Список хостов status = Column(String, nullable=False) # success, failed, running started_at = Column(DateTime, default=datetime.utcnow, nullable=False) finished_at = Column(DateTime) duration = Column(Integer) output = Column(Text) error = Column(Text) user = Column(String) extra_data = Column(JSON) class ExportHistory(Base): """История экспорта ролей""" __tablename__ = "export_history" id = Column(Integer, primary_key=True, index=True) role_name = Column(String, nullable=False, index=True) repo_url = Column(String, nullable=False) branch = Column(String) version = Column(String) commit_hash = Column(String) status = Column(String, nullable=False) # success, failed started_at = Column(DateTime, default=datetime.utcnow, nullable=False) finished_at = Column(DateTime) user = Column(String) extra_data = Column(JSON) class ImportHistory(Base): """История импорта ролей""" __tablename__ = "import_history" id = Column(Integer, primary_key=True, index=True) role_name = Column(String, nullable=False, index=True) source_type = Column(String, nullable=False) # git, galaxy source_url = Column(String) status = Column(String, nullable=False) # success, failed started_at = Column(DateTime, default=datetime.utcnow, nullable=False) finished_at = Column(DateTime) user = Column(String) extra_data = Column(JSON) class Playbook(Base): """Playbook - объединение ролей для тестирования и деплоя""" __tablename__ = "playbooks" id = Column(Integer, primary_key=True, index=True) name = Column(String, unique=True, nullable=False, index=True) description = Column(Text) content = Column(Text, nullable=False) # YAML содержимое playbook roles = Column(JSON, nullable=False) # Список ролей в playbook variables = Column(JSON) # Переменные для playbook inventory = Column(Text) # Инвентарь для playbook status = Column(String, default="active") # active, archived created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) created_by = Column(String) updated_by = Column(String) extra_data = Column(JSON) # Связи test_runs = relationship("PlaybookTestRun", back_populates="playbook") deployments = relationship("PlaybookDeployment", back_populates="playbook") class PlaybookTestRun(Base): """Результаты тестирования playbook""" __tablename__ = "playbook_test_runs" id = Column(Integer, primary_key=True, index=True) playbook_id = Column(Integer, ForeignKey("playbooks.id"), nullable=False) preset_name = Column(String, index=True) status = Column(String, nullable=False) # running, success, failed, cancelled started_at = Column(DateTime, default=datetime.utcnow, nullable=False) finished_at = Column(DateTime) duration = Column(Integer) # в секундах output = Column(Text) error = Column(Text) returncode = Column(Integer) user = Column(String) extra_data = Column(JSON) # Связи playbook = relationship("Playbook", back_populates="test_runs") class PlaybookDeployment(Base): """Результаты деплоя playbook""" __tablename__ = "playbook_deployments" id = Column(Integer, primary_key=True, index=True) playbook_id = Column(Integer, ForeignKey("playbooks.id"), nullable=False) inventory = Column(Text) hosts = Column(JSON) # Список хостов status = Column(String, nullable=False) # running, success, failed, cancelled started_at = Column(DateTime, default=datetime.utcnow, nullable=False) finished_at = Column(DateTime) duration = Column(Integer) # в секундах output = Column(Text) error = Column(Text) returncode = Column(Integer) user = Column(String) extra_data = Column(JSON) # Связи playbook = relationship("Playbook", back_populates="deployments") class Dockerfile(Base): """Dockerfile для образов тестирования""" __tablename__ = "dockerfiles" id = Column(Integer, primary_key=True, index=True) name = Column(String, unique=True, nullable=False, index=True) # ubuntu22, centos8, etc. description = Column(Text) content = Column(Text, nullable=False) # Содержимое Dockerfile base_image = Column(String) # Базовый образ tags = Column(JSON) # Теги для образа platforms = Column(JSON) # Платформы для сборки (по умолчанию: ["linux/amd64", "linux/386", "linux/arm64"]) status = Column(String, default="active") # active, archived created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) created_by = Column(String) updated_by = Column(String) extra_data = Column(JSON) class DockerfileBuildLog(Base): """Логи сборки Dockerfile""" __tablename__ = "dockerfile_build_logs" id = Column(Integer, primary_key=True, index=True) dockerfile_id = Column(Integer, ForeignKey("dockerfiles.id"), nullable=False, index=True) image_name = Column(String, nullable=False, index=True) tag = Column(String, default="latest") platforms = Column(JSON) # Список платформ status = Column(String, nullable=False, default="running") # running, success, failed logs = Column(Text) # Полные логи сборки started_at = Column(DateTime, default=datetime.utcnow, nullable=False, index=True) finished_at = Column(DateTime) duration = Column(Integer) # в секундах returncode = Column(Integer) user = Column(String) extra_data = Column(JSON) # Связи dockerfile = relationship("Dockerfile", backref="build_logs") class Role(Base): """Ansible роли""" __tablename__ = "roles" id = Column(Integer, primary_key=True, index=True) name = Column(String, unique=True, nullable=False, index=True) # Имя роли description = Column(Text) # Описание роли content = Column(JSON, nullable=False) # Содержимое роли в виде JSON структуры {file_path: content} # Флаги доступа is_global = Column(Boolean, default=False, nullable=False, index=True) # Доступно для всех пользователей is_personal = Column(Boolean, default=False, nullable=False, index=True) # Личная роль пользователя groups = Column(JSON) # Список групп, которым доступна роль (если is_personal=False и is_global=False) user_id = Column(Integer, ForeignKey("users.id"), nullable=True, index=True) # Владелец роли (если is_personal=True) # Метаданные author = Column(String) # Автор роли platforms = Column(JSON) # Поддерживаемые платформы galaxy_info = Column(JSON) # Информация для Ansible Galaxy status = Column(String, default="active") # active, archived created_at = Column(DateTime, default=datetime.utcnow, nullable=False, index=True) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) created_by = Column(String) updated_by = Column(String) extra_data = Column(JSON) # Связи user = relationship("User", backref="roles") class Preset(Base): """Preset для Molecule тестирования""" __tablename__ = "presets" id = Column(Integer, primary_key=True, index=True) name = Column(String, unique=True, nullable=False, index=True) # minimal, default, etc. category = Column(String, default="main", index=True) # main, k8s description = Column(Text) # Описание из комментария #description: content = Column(Text, nullable=False) # YAML содержимое preset'а docker_network = Column(String) # Имя Docker сети hosts = Column(JSON) # Список хостов images = Column(JSON) # Словарь образов systemd_defaults = Column(JSON) # Настройки systemd kind_clusters = Column(JSON) # Kubernetes кластеры (для k8s preset'ов) status = Column(String, default="active") # active, archived created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) created_by = Column(String) updated_by = Column(String) extra_data = Column(JSON)