""" Infrastructure change model for tracking infrastructure modifications. Tracks changes to infrastructure including DNS, firewall, routing, SSL, containers, and other infrastructure components with audit trail and rollback procedures. """ from datetime import datetime from typing import TYPE_CHECKING, Optional from sqlalchemy import Boolean, CHAR, CheckConstraint, ForeignKey, Index, String, Text, TIMESTAMP from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func from .base import Base, UUIDMixin if TYPE_CHECKING: from .infrastructure import Infrastructure from .session import Session from .work_item import WorkItem class InfrastructureChange(Base, UUIDMixin): """ Infrastructure change model for audit trail of infrastructure modifications. Records changes to infrastructure components including DNS configuration, firewall rules, routing tables, SSL certificates, containers, service configurations, hardware, network, and storage. Tracks before/after state, rollback procedures, and verification status for operational safety. Attributes: work_item_id: Foreign key to work_items table (required) session_id: Foreign key to sessions table (required) infrastructure_id: Foreign key to infrastructure table change_type: Type of infrastructure change target_system: System or component that was modified before_state: State before the change (configuration snapshot) after_state: State after the change (configuration snapshot) is_permanent: Whether this is a permanent change or temporary rollback_procedure: Instructions for rolling back this change verification_performed: Whether verification was performed after change verification_notes: Notes about verification testing created_at: When the change was made work_item: Relationship to WorkItem model session: Relationship to Session model infrastructure: Relationship to Infrastructure model """ __tablename__ = "infrastructure_changes" # Foreign keys work_item_id: Mapped[str] = mapped_column( CHAR(36), ForeignKey("work_items.id", ondelete="CASCADE"), nullable=False, doc="Foreign key to work_items table (required)" ) session_id: Mapped[str] = mapped_column( CHAR(36), ForeignKey("sessions.id", ondelete="CASCADE"), nullable=False, doc="Foreign key to sessions table (required)" ) infrastructure_id: Mapped[Optional[str]] = mapped_column( CHAR(36), ForeignKey("infrastructure.id", ondelete="SET NULL"), doc="Foreign key to infrastructure table" ) # Change details change_type: Mapped[Optional[str]] = mapped_column( String(50), doc="Type of change: dns, firewall, routing, ssl, container, service_config, hardware, network, storage" ) target_system: Mapped[str] = mapped_column( String(255), nullable=False, doc="System or component that was modified (e.g., 'jupiter', 'UDM-Pro', 'web-container')" ) # State tracking before_state: Mapped[Optional[str]] = mapped_column( Text, doc="Configuration or state before the change (snapshot, config dump, etc.)" ) after_state: Mapped[Optional[str]] = mapped_column( Text, doc="Configuration or state after the change (snapshot, config dump, etc.)" ) # Change characteristics is_permanent: Mapped[bool] = mapped_column( Boolean, default=True, server_default="1", nullable=False, doc="Whether this is a permanent change or temporary (e.g., for testing)" ) rollback_procedure: Mapped[Optional[str]] = mapped_column( Text, doc="Instructions for rolling back this change if needed" ) # Verification verification_performed: Mapped[bool] = mapped_column( Boolean, default=False, server_default="0", nullable=False, doc="Whether verification testing was performed after the change" ) verification_notes: Mapped[Optional[str]] = mapped_column( Text, doc="Notes about verification testing (what was tested, results, etc.)" ) # Timestamp created_at: Mapped[datetime] = mapped_column( TIMESTAMP, nullable=False, server_default=func.now(), doc="When the change was made" ) # Relationships work_item: Mapped["WorkItem"] = relationship( "WorkItem", back_populates="infrastructure_changes", doc="Relationship to WorkItem model" ) session: Mapped["Session"] = relationship( "Session", back_populates="infrastructure_changes", doc="Relationship to Session model" ) infrastructure: Mapped[Optional["Infrastructure"]] = relationship( "Infrastructure", back_populates="infrastructure_changes", doc="Relationship to Infrastructure model" ) # Constraints and indexes __table_args__ = ( CheckConstraint( "change_type IN ('dns', 'firewall', 'routing', 'ssl', 'container', 'service_config', 'hardware', 'network', 'storage')", name="ck_infrastructure_changes_type" ), Index("idx_infra_changes_work_item", "work_item_id"), Index("idx_infra_changes_session", "session_id"), Index("idx_infra_changes_infrastructure", "infrastructure_id"), ) def __repr__(self) -> str: """String representation of the infrastructure change.""" return f""