Files
ipam/backend/app/schemas/network.py
T
Your Name 1e1e5957e8 fix(ipam): 修复IP管理页面网段筛选不生效及下拉框过窄
- Ips.vue: el-select 添加 width: 280px,修复网段下拉框过窄
- Ips.vue: query 参数 networkId → network_id(与 FastAPI snake_case 一致)
- Scan.vue: el-select 用 network.id 代替整个 network 对象作为 value,修复切换网段不生效
- enhanced_scan_service.py: 替换失效的 scapy ARP 扫描为 arp-scan 命令;扫描结果自动创建缺失 IP 记录
2026-07-18 09:45:30 +08:00

174 lines
4.1 KiB
Python

from pydantic import BaseModel, Field, field_validator
from typing import Optional, List, Dict, Any
from datetime import datetime
import ipaddress
from app.models.network import IPStatus, TaskStatus, TaskType
# ========== 网段相关 Schemas ==========
class NetworkBase(BaseModel):
cidr: str = Field(..., description="网段CIDR,如: 192.168.1.0/24")
name: Optional[str] = Field(None, max_length=100, description="网段名称")
description: Optional[str] = Field(None, description="描述")
group_name: Optional[str] = Field(None, max_length=100, description="分组名称")
gateway: Optional[str] = Field(None, max_length=50, description="网关地址")
vlan_id: Optional[int] = Field(None, description="VLAN ID")
@field_validator('cidr')
@classmethod
def validate_cidr(cls, v):
try:
network = ipaddress.ip_network(v, strict=False)
return str(network)
except ValueError:
raise ValueError(f"无效的CIDR格式: {v}")
@field_validator('gateway')
@classmethod
def validate_gateway(cls, v):
if v:
try:
ipaddress.ip_address(v)
except ValueError:
raise ValueError(f"无效的IP地址: {v}")
return v
class NetworkCreate(NetworkBase):
pass
class NetworkUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
group_name: Optional[str] = None
gateway: Optional[str] = None
vlan_id: Optional[int] = None
class Network(NetworkBase):
id: int
total_ips: int
used_ips: int
reserved_ips: int
created_at: datetime
updated_at: Optional[datetime]
class Config:
from_attributes = True
class NetworkStats(BaseModel):
"""网段统计信息"""
id: int
cidr: str
name: Optional[str]
total_ips: int
used_ips: int
reserved_ips: int
available_ips: int
usage_percent: float
group_name: Optional[str]
vlan_id: Optional[int]
class Config:
from_attributes = True
class NetworkListResponse(BaseModel):
total: int
items: List[Network]
# ========== IP地址相关 Schemas ==========
class IPAddressBase(BaseModel):
ip_address: str
mac_address: Optional[str] = None
hostname: Optional[str] = None
owner: Optional[str] = None
business_type: Optional[str] = None
notes: Optional[str] = None
custom_fields: Dict[str, Any] = Field(default_factory=dict)
switch_name: Optional[str] = None
switch_port: Optional[str] = None
vendor: Optional[str] = None
class IPAddressCreate(IPAddressBase):
network_id: int
status: IPStatus = IPStatus.AVAILABLE
class IPAddressUpdate(BaseModel):
mac_address: Optional[str] = None
hostname: Optional[str] = None
status: Optional[IPStatus] = None
owner: Optional[str] = None
business_type: Optional[str] = None
notes: Optional[str] = None
custom_fields: Optional[Dict[str, Any]] = None
class IPAddress(IPAddressBase):
id: int
network_id: int
status: IPStatus
last_seen: Optional[datetime]
first_seen: datetime
created_at: datetime
updated_at: Optional[datetime]
class Config:
from_attributes = True
class IPAddressListResponse(BaseModel):
total: int
items: List[IPAddress]
# ========== 扫描任务相关 Schemas ==========
class ScanTaskBase(BaseModel):
task_type: TaskType
network_id: Optional[int] = None
class ScanTaskCreate(ScanTaskBase):
pass
class ScanTask(BaseModel):
id: int
network_id: Optional[int]
task_type: TaskType
status: TaskStatus
progress: int
celery_task_id: Optional[str]
total_count: int
success_count: int
failed_count: int
started_at: Optional[datetime]
completed_at: Optional[datetime]
error_message: Optional[str]
created_at: datetime
class Config:
from_attributes = True
class ScanTaskListResponse(BaseModel):
total: int
items: List[ScanTask]
class ScanResult(BaseModel):
"""单次扫描结果"""
ip_address: str
status: str
mac_address: Optional[str] = None
hostname: Optional[str] = None
response_time: Optional[float] = None