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 记录
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
from sqlalchemy import Column, Integer, String, Text, DateTime, Enum, ForeignKey, JSON, Boolean
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
from app.core.database import Base
|
||||
import enum
|
||||
|
||||
|
||||
class NetworkGroup(str, enum.Enum):
|
||||
"""网段分组枚举"""
|
||||
PRODUCTION = "production"
|
||||
OFFICE = "office"
|
||||
TEST = "test"
|
||||
MANAGEMENT = "management"
|
||||
OTHER = "other"
|
||||
|
||||
|
||||
class IPStatus(str, enum.Enum):
|
||||
"""IP状态枚举"""
|
||||
AVAILABLE = "available"
|
||||
ONLINE = "online"
|
||||
OFFLINE = "offline"
|
||||
RESERVED = "reserved"
|
||||
|
||||
|
||||
class TaskStatus(str, enum.Enum):
|
||||
"""任务状态枚举"""
|
||||
PENDING = "pending"
|
||||
RUNNING = "running"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
class TaskType(str, enum.Enum):
|
||||
"""任务类型枚举"""
|
||||
PING = "ping"
|
||||
ARP = "arp"
|
||||
SNMP = "snmp"
|
||||
|
||||
|
||||
class Network(Base):
|
||||
"""网段模型"""
|
||||
__tablename__ = "networks"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
cidr = Column(String(50), unique=True, index=True, nullable=False) # 如: 192.168.1.0/24
|
||||
name = Column(String(100), index=True)
|
||||
description = Column(Text, nullable=True)
|
||||
group_name = Column(String(100), index=True, nullable=True) # 分组名称
|
||||
gateway = Column(String(50), nullable=True)
|
||||
vlan_id = Column(Integer, nullable=True)
|
||||
total_ips = Column(Integer, default=0)
|
||||
used_ips = Column(Integer, default=0)
|
||||
reserved_ips = Column(Integer, default=0)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# 关系
|
||||
ip_addresses = relationship("IPAddress", back_populates="network", cascade="all, delete-orphan")
|
||||
scan_tasks = relationship("ScanTask", back_populates="network")
|
||||
|
||||
|
||||
class IPAddress(Base):
|
||||
"""IP地址模型"""
|
||||
__tablename__ = "ip_addresses"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
network_id = Column(Integer, ForeignKey("networks.id"), nullable=False)
|
||||
ip_address = Column(String(50), unique=True, index=True, nullable=False)
|
||||
mac_address = Column(String(50), nullable=True, index=True)
|
||||
hostname = Column(String(255), nullable=True)
|
||||
status = Column(Enum(IPStatus), default=IPStatus.AVAILABLE)
|
||||
|
||||
# 业务属性
|
||||
owner = Column(String(100), nullable=True)
|
||||
business_type = Column(String(50), nullable=True)
|
||||
notes = Column(Text, nullable=True)
|
||||
custom_fields = Column(JSON, default=dict)
|
||||
|
||||
# 扫描信息
|
||||
last_seen = Column(DateTime(timezone=True), nullable=True)
|
||||
first_seen = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
# 物理位置信息
|
||||
switch_name = Column(String(100), nullable=True)
|
||||
switch_port = Column(String(50), nullable=True)
|
||||
vendor = Column(String(100), nullable=True) # MAC OUI 解析的厂商
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# 关系
|
||||
network = relationship("Network", back_populates="ip_addresses")
|
||||
|
||||
|
||||
class ScanTask(Base):
|
||||
"""扫描任务模型"""
|
||||
__tablename__ = "scan_tasks"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
network_id = Column(Integer, ForeignKey("networks.id"), nullable=True)
|
||||
task_type = Column(Enum(TaskType), nullable=False)
|
||||
status = Column(Enum(TaskStatus), default=TaskStatus.PENDING)
|
||||
progress = Column(Integer, default=0) # 0-100
|
||||
celery_task_id = Column(String(100), nullable=True)
|
||||
|
||||
# 统计信息
|
||||
total_count = Column(Integer, default=0)
|
||||
success_count = Column(Integer, default=0)
|
||||
failed_count = Column(Integer, default=0)
|
||||
|
||||
started_at = Column(DateTime(timezone=True), nullable=True)
|
||||
completed_at = Column(DateTime(timezone=True), nullable=True)
|
||||
error_message = Column(Text, nullable=True)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
# 关系
|
||||
network = relationship("Network", back_populates="scan_tasks")
|
||||
Reference in New Issue
Block a user