""" Network service layer for business logic and database operations. This module handles all database operations for networks, providing a clean separation between the API routes and data access layer. """ from typing import Optional from uuid import UUID from fastapi import HTTPException, status from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import Session from api.models.network import Network from api.models.site import Site from api.schemas.network import NetworkCreate, NetworkUpdate def get_networks(db: Session, skip: int = 0, limit: int = 100) -> tuple[list[Network], int]: """ Retrieve a paginated list of networks. Args: db: Database session skip: Number of records to skip (for pagination) limit: Maximum number of records to return Returns: tuple: (list of networks, total count) Example: ```python networks, total = get_networks(db, skip=0, limit=50) print(f"Retrieved {len(networks)} of {total} networks") ``` """ # Get total count total = db.query(Network).count() # Get paginated results, ordered by created_at descending (newest first) networks = ( db.query(Network) .order_by(Network.created_at.desc()) .offset(skip) .limit(limit) .all() ) return networks, total def get_network_by_id(db: Session, network_id: UUID) -> Network: """ Retrieve a single network by its ID. Args: db: Database session network_id: UUID of the network to retrieve Returns: Network: The network object Raises: HTTPException: 404 if network not found Example: ```python network = get_network_by_id(db, network_id) print(f"Found network: {network.network_name}") ``` """ network = db.query(Network).filter(Network.id == str(network_id)).first() if not network: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Network with ID {network_id} not found" ) return network def get_networks_by_site(db: Session, site_id: UUID, skip: int = 0, limit: int = 100) -> tuple[list[Network], int]: """ Retrieve networks belonging to a specific site. Args: db: Database session site_id: UUID of the site skip: Number of records to skip (for pagination) limit: Maximum number of records to return Returns: tuple: (list of networks, total count for this site) Raises: HTTPException: 404 if site not found Example: ```python networks, total = get_networks_by_site(db, site_id, skip=0, limit=50) print(f"Retrieved {len(networks)} of {total} networks for site") ``` """ # Verify site exists site = db.query(Site).filter(Site.id == str(site_id)).first() if not site: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Site with ID {site_id} not found" ) # Get total count for this site total = db.query(Network).filter(Network.site_id == str(site_id)).count() # Get paginated results networks = ( db.query(Network) .filter(Network.site_id == str(site_id)) .order_by(Network.created_at.desc()) .offset(skip) .limit(limit) .all() ) return networks, total def get_networks_by_type(db: Session, network_type: str, skip: int = 0, limit: int = 100) -> tuple[list[Network], int]: """ Retrieve networks of a specific type. Args: db: Database session network_type: Type of network (lan, vpn, vlan, isolated, dmz) skip: Number of records to skip (for pagination) limit: Maximum number of records to return Returns: tuple: (list of networks, total count for this type) Example: ```python networks, total = get_networks_by_type(db, "vlan", skip=0, limit=50) print(f"Retrieved {len(networks)} of {total} VLAN networks") ``` """ # Get total count for this type total = db.query(Network).filter(Network.network_type == network_type).count() # Get paginated results networks = ( db.query(Network) .filter(Network.network_type == network_type) .order_by(Network.created_at.desc()) .offset(skip) .limit(limit) .all() ) return networks, total def create_network(db: Session, network_data: NetworkCreate) -> Network: """ Create a new network. Args: db: Database session network_data: Network creation data Returns: Network: The created network object Raises: HTTPException: 404 if site not found HTTPException: 500 if database error occurs Example: ```python network_data = NetworkCreate( site_id="123e4567-e89b-12d3-a456-426614174000", network_name="Main LAN", network_type="lan", cidr="192.168.1.0/24" ) network = create_network(db, network_data) print(f"Created network: {network.id}") ``` """ # Verify site exists if provided if network_data.site_id: site = db.query(Site).filter(Site.id == str(network_data.site_id)).first() if not site: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Site with ID {network_data.site_id} not found" ) try: # Create new network instance db_network = Network(**network_data.model_dump()) # Add to database db.add(db_network) db.commit() db.refresh(db_network) return db_network except IntegrityError as e: db.rollback() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Database error: {str(e)}" ) except Exception as e: db.rollback() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to create network: {str(e)}" ) def update_network(db: Session, network_id: UUID, network_data: NetworkUpdate) -> Network: """ Update an existing network. Args: db: Database session network_id: UUID of the network to update network_data: Network update data (only provided fields will be updated) Returns: Network: The updated network object Raises: HTTPException: 404 if network or site not found HTTPException: 500 if database error occurs Example: ```python update_data = NetworkUpdate( network_name="Main LAN - Upgraded", gateway_ip="192.168.1.1" ) network = update_network(db, network_id, update_data) print(f"Updated network: {network.network_name}") ``` """ # Get existing network network = get_network_by_id(db, network_id) try: # Update only provided fields update_data = network_data.model_dump(exclude_unset=True) # If updating site_id, verify new site exists if "site_id" in update_data and update_data["site_id"] is not None: site = db.query(Site).filter(Site.id == str(update_data["site_id"])).first() if not site: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Site with ID {update_data['site_id']} not found" ) # Apply updates for field, value in update_data.items(): setattr(network, field, value) db.commit() db.refresh(network) return network except HTTPException: db.rollback() raise except IntegrityError as e: db.rollback() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Database error: {str(e)}" ) except Exception as e: db.rollback() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to update network: {str(e)}" ) def delete_network(db: Session, network_id: UUID) -> dict: """ Delete a network by its ID. Args: db: Database session network_id: UUID of the network to delete Returns: dict: Success message Raises: HTTPException: 404 if network not found HTTPException: 500 if database error occurs Example: ```python result = delete_network(db, network_id) print(result["message"]) # "Network deleted successfully" ``` """ # Get existing network (raises 404 if not found) network = get_network_by_id(db, network_id) try: db.delete(network) db.commit() return { "message": "Network deleted successfully", "network_id": str(network_id) } except Exception as e: db.rollback() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to delete network: {str(e)}" )