from datetime import datetime from typing import Any, Dict, Generic, List, Literal, Optional, TypeVar from pydantic import BaseModel, Field T = TypeVar('T') JobStatus = Literal['pending', 'running', 'completed', 'failed', 'cancelled'] ObjectStatus = Literal['active', 'planned', 'inactive', 'retired', 'unknown'] ObjectType = Literal[ 'site', 'area', 'rack', 'physical_host', 'vm', 'lxc', 'container', 'app', 'switch', 'firewall', 'router', 'ap', 'camera', 'storage', 'other' ] class HealthCheck(BaseModel): status: Literal['pass', 'warn', 'fail'] latency_ms: Optional[float] = None detail: Optional[str] = None class HealthResponse(BaseModel): status: Literal['pass', 'warn', 'fail'] service: str version: Optional[str] = None time: datetime uptime_s: Optional[float] = None checks: Dict[str, HealthCheck] = Field(default_factory=dict) class NetObject(BaseModel): id: str name: str slug: Optional[str] = None type: ObjectType parent_id: Optional[str] = None status: ObjectStatus vendor: Optional[str] = None model: Optional[str] = None serial: Optional[str] = None notes: Optional[str] = None created_at: datetime updated_at: datetime class IPAddress(BaseModel): id: str address: str version: Literal[4, 6] subnet_id: Optional[str] = None object_id: Optional[str] = None interface_id: Optional[str] = None hostname: Optional[str] = None source: Literal['manual', 'snmp', 'arp', 'lldp', 'nmap', 'import', 'other'] status: Literal['assigned', 'reserved', 'free', 'unknown'] = 'unknown' class Subnet(BaseModel): id: str name: str cidr: str gateway: Optional[str] = None vlan_id: Optional[int] = None notes: Optional[str] = None class Link(BaseModel): id: str source_object_id: str target_object_id: str source_interface_id: Optional[str] = None target_interface_id: Optional[str] = None type: Literal['physical', 'logical', 'hosted_on', 'member_of', 'uplink', 'other'] confidence: float = 1.0 source: Literal['manual', 'snmp', 'lldp', 'arp', 'import', 'inference'] class Job(BaseModel): id: str type: str status: JobStatus progress: int = 0 message: Optional[str] = None error: Optional[str] = None payload: Optional[Dict[str, Any]] = None result: Optional[Dict[str, Any]] = None created_at: datetime started_at: Optional[datetime] = None finished_at: Optional[datetime] = None class DiscoveryProfile(BaseModel): id: str name: str strategy: Literal['fast', 'balanced', 'deep', 'custom'] use_icmp: bool = True use_arp: bool = True use_snmp: bool = False use_lldp: bool = False use_portscan: bool = False notes: Optional[str] = None class DiscoveryRun(BaseModel): id: str profile_id: str subnet_ids: List[str] = Field(default_factory=list) job_id: Optional[str] = None status: JobStatus summary: Optional[Dict[str, Any]] = None created_at: datetime started_at: Optional[datetime] = None finished_at: Optional[datetime] = None class ListResponse(BaseModel, Generic[T]): count: int items: List[T] page: Optional[int] = None page_size: Optional[int] = None total: Optional[int] = None class EntityResponse(BaseModel, Generic[T]): item: T