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

121 lines
4.5 KiB
Python

from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Enum, Boolean, Float
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.core.database import Base
import enum
class AlertType(str, enum.Enum):
"""告警类型"""
IP_CONFLICT = "ip_conflict" # IP 冲突
UNAUTHORIZED_ACCESS = "unauthorized_access" # 未授权接入
SUBNET_FULL = "subnet_full" # 网段耗尽
SCAN_FAILED = "scan_failed" # 扫描失败
DEVICE_OFFLINE = "device_offline" # 设备离线
NEW_DEVICE_DETECTED = "new_device_detected" # 新设备发现
MAC_CHANGED = "mac_changed" # MAC 地址变化
class AlertSeverity(str, enum.Enum):
"""告警级别"""
INFO = "info" # 信息
WARNING = "warning" # 警告
ERROR = "error" # 错误
CRITICAL = "critical" # 严重
class AlertStatus(str, enum.Enum):
"""告警状态"""
ACTIVE = "active" # 活跃
ACKNOWLEDGED = "acknowledged" # 已确认
RESOLVED = "resolved" # 已解决
IGNORED = "ignored" # 已忽略
class Alert(Base):
"""告警"""
__tablename__ = "alerts"
id = Column(Integer, primary_key=True, index=True)
alert_type = Column(Enum(AlertType), nullable=False, index=True)
severity = Column(Enum(AlertSeverity), default=AlertSeverity.WARNING, index=True)
status = Column(Enum(AlertStatus), default=AlertStatus.ACTIVE, index=True)
title = Column(String(255), nullable=False)
message = Column(Text)
# 关联对象
network_id = Column(Integer, ForeignKey("networks.id"), nullable=True)
ip_address_id = Column(Integer, ForeignKey("ip_addresses.id"), nullable=True)
device_id = Column(Integer, ForeignKey("network_devices.id"), nullable=True)
ip_address_str = Column(String(50), nullable=True) # 冗余存储便于查询
# 关联的 IP/MAC 信息
mac_address = Column(String(50), nullable=True)
conflicting_mac = Column(String(50), nullable=True) # 用于 IP 冲突告警
# 统计信息
usage_percent = Column(Float, nullable=True) # 网段使用率告警时存储
# 处理信息
acknowledged_by = Column(String(100), nullable=True)
acknowledged_at = Column(DateTime(timezone=True), nullable=True)
resolved_by = Column(String(100), nullable=True)
resolved_at = Column(DateTime(timezone=True), nullable=True)
resolution_notes = Column(Text, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now(), index=True)
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# 关系
network = relationship("Network")
ip_address = relationship("IPAddress")
device = relationship("NetworkDevice")
class AlertRule(Base):
"""告警规则配置"""
__tablename__ = "alert_rules"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(100), unique=True, nullable=False)
description = Column(Text, nullable=True)
alert_type = Column(Enum(AlertType), nullable=False)
severity = Column(Enum(AlertSeverity), default=AlertSeverity.WARNING)
# 规则条件
enabled = Column(Boolean, default=True)
# 阈值配置
subnet_usage_threshold = Column(Float, default=90.0) # 网段使用率阈值 %
scan_failure_threshold = Column(Integer, default=3) # 扫描失败次数阈值
inactivity_threshold_minutes = Column(Integer, default=60) # 设备不活动阈值(分钟)
# MAC 白名单(用于未授权接入检测)
mac_whitelist_enabled = Column(Boolean, default=False)
mac_whitelist = Column(Text, nullable=True) # JSON 格式的 MAC 列表
# 通知配置
notify_email = Column(Boolean, default=False)
notify_webhook = Column(Boolean, default=False)
notification_channels = Column(Text, nullable=True) # JSON 格式的通知渠道
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
class WhitelistedMAC(Base):
"""MAC 地址白名单"""
__tablename__ = "whitelisted_macs"
id = Column(Integer, primary_key=True, index=True)
mac_address = Column(String(50), unique=True, nullable=False, index=True)
description = Column(String(255), nullable=True)
owner = Column(String(100), nullable=True)
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())