Files
ipam/backend/app/models/snmp.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

191 lines
6.7 KiB
Python

from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Enum, Boolean
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.core.database import Base
import enum
class SNMPVersion(str, enum.Enum):
"""SNMP 版本"""
V2C = "v2c"
V3 = "v3"
class SNMPAuthProtocol(str, enum.Enum):
"""SNMP V3 认证协议"""
MD5 = "md5"
SHA = "sha"
SHA256 = "sha256"
class SNMPPrivProtocol(str, enum.Enum):
"""SNMP V3 加密协议"""
DES = "des"
AES = "aes"
AES256 = "aes256"
class DeviceType(str, enum.Enum):
"""设备类型"""
ROUTER = "router"
CORE_SWITCH = "core_switch"
DISTRIBUTION_SWITCH = "distribution_switch"
ACCESS_SWITCH = "access_switch"
FIREWALL = "firewall"
LOAD_BALANCER = "load_balancer"
OTHER = "other"
class SNMPCredential(Base):
"""SNMP 凭据"""
__tablename__ = "snmp_credentials"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(100), unique=True, index=True, nullable=False)
description = Column(Text, nullable=True)
# 版本
version = Column(Enum(SNMPVersion), default=SNMPVersion.V2C, nullable=False)
# V2C 配置
community_string = Column(String(255), nullable=True)
# V3 配置
username = Column(String(100), nullable=True)
auth_password = Column(String(255), nullable=True)
auth_protocol = Column(Enum(SNMPAuthProtocol), nullable=True)
priv_password = Column(String(255), nullable=True)
priv_protocol = Column(Enum(SNMPPrivProtocol), nullable=True)
# 超时和重试
timeout = Column(Integer, default=3)
retries = Column(Integer, default=2)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# 关系
devices = relationship("NetworkDevice", back_populates="snmp_credential")
class NetworkDevice(Base):
"""网络设备"""
__tablename__ = "network_devices"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(100), unique=True, index=True, nullable=False)
description = Column(Text, nullable=True)
# 连接信息
ip_address = Column(String(50), index=True, nullable=False)
port = Column(Integer, default=161)
# 设备信息
device_type = Column(Enum(DeviceType), default=DeviceType.OTHER)
vendor = Column(String(100), nullable=True)
model = Column(String(100), nullable=True)
firmware_version = Column(String(100), nullable=True)
serial_number = Column(String(100), nullable=True)
location = Column(String(200), nullable=True)
# SNMP 凭据
snmp_credential_id = Column(Integer, ForeignKey("snmp_credentials.id"), nullable=True)
# 状态
is_active = Column(Boolean, default=True)
last_polled_at = Column(DateTime(timezone=True), nullable=True)
last_successful_poll = Column(DateTime(timezone=True), nullable=True)
# 采集配置
arp_poll_interval = Column(Integer, default=300) # ARP表采集间隔(秒)
mac_poll_interval = Column(Integer, default=300) # MAC表采集间隔(秒)
interface_poll_interval = Column(Integer, default=300) # 接口信息采集间隔
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# 关系
snmp_credential = relationship("SNMPCredential", back_populates="devices")
arp_entries = relationship("ARPEntry", back_populates="device", cascade="all, delete-orphan")
mac_entries = relationship("MACAddressTable", back_populates="device", cascade="all, delete-orphan")
interfaces = relationship("DeviceInterface", back_populates="device", cascade="all, delete-orphan")
class ARPEntry(Base):
"""ARP 表条目 (IP -> MAC 映射)"""
__tablename__ = "arp_entries"
id = Column(Integer, primary_key=True, index=True)
device_id = Column(Integer, ForeignKey("network_devices.id"), nullable=False)
ip_address = Column(String(50), index=True, nullable=False)
mac_address = Column(String(50), index=True, nullable=False)
interface = Column(String(100), nullable=True) # 接口名称/索引
vlan_id = Column(Integer, nullable=True)
# 老化信息
age = Column(Integer, nullable=True) # 秒
is_static = Column(Boolean, default=False)
discovered_at = Column(DateTime(timezone=True), server_default=func.now())
last_seen = Column(DateTime(timezone=True), server_default=func.now())
# 关系
device = relationship("NetworkDevice", back_populates="arp_entries")
class MACAddressTable(Base):
"""MAC 地址表 (MAC -> 端口 映射)"""
__tablename__ = "mac_address_table"
id = Column(Integer, primary_key=True, index=True)
device_id = Column(Integer, ForeignKey("network_devices.id"), nullable=False)
mac_address = Column(String(50), index=True, nullable=False)
vlan_id = Column(Integer, nullable=True)
interface = Column(String(100), nullable=True) # 接口名称
port_number = Column(Integer, nullable=True) # 端口号
# 类型: learned, static, management, etc.
entry_type = Column(String(50), nullable=True)
discovered_at = Column(DateTime(timezone=True), server_default=func.now())
last_seen = Column(DateTime(timezone=True), server_default=func.now())
# 关系
device = relationship("NetworkDevice", back_populates="mac_entries")
class DeviceInterface(Base):
"""设备接口信息"""
__tablename__ = "device_interfaces"
id = Column(Integer, primary_key=True, index=True)
device_id = Column(Integer, ForeignKey("network_devices.id"), nullable=False)
if_index = Column(Integer, nullable=False) # ifIndex
if_name = Column(String(100), nullable=True) # ifName
if_descr = Column(String(255), nullable=True) # ifDescr
if_type = Column(String(50), nullable=True) # ifType
if_mtu = Column(Integer, nullable=True) # ifMtu
if_speed = Column(Integer, nullable=True) # ifSpeed (bps)
if_phys_address = Column(String(50), nullable=True) # ifPhysAddress (MAC)
if_admin_status = Column(String(50), nullable=True) # up/down/testing
if_oper_status = Column(String(50), nullable=True) # up/down/testing
# IP 地址
ip_address = Column(String(50), nullable=True)
ip_netmask = Column(String(50), nullable=True)
# 统计
if_in_octets = Column(Integer, nullable=True)
if_out_octets = Column(Integer, nullable=True)
if_in_errors = Column(Integer, nullable=True)
if_out_errors = Column(Integer, nullable=True)
last_polled_at = Column(DateTime(timezone=True), nullable=True)
# 关系
device = relationship("NetworkDevice", back_populates="interfaces")