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,143 @@
|
||||
from typing import List, Optional, Dict, Any
|
||||
from sqlalchemy.orm import Session
|
||||
from datetime import datetime
|
||||
|
||||
from app.models.network import IPAddress as IPAddressModel
|
||||
from app.models.network import Network as NetworkModel
|
||||
from app.models.network import IPStatus
|
||||
from app.schemas.network import IPAddressUpdate, IPAddressCreate
|
||||
|
||||
|
||||
class IPService:
|
||||
"""IP地址管理服务"""
|
||||
|
||||
@staticmethod
|
||||
def get_by_id(db: Session, ip_id: int) -> Optional[IPAddressModel]:
|
||||
"""根据ID获取IP"""
|
||||
return db.query(IPAddressModel).filter(IPAddressModel.id == ip_id).first()
|
||||
|
||||
@staticmethod
|
||||
def get_by_ip(db: Session, ip_address: str) -> Optional[IPAddressModel]:
|
||||
"""根据IP地址获取"""
|
||||
return db.query(IPAddressModel).filter(IPAddressModel.ip_address == ip_address).first()
|
||||
|
||||
@staticmethod
|
||||
def get_list(
|
||||
db: Session,
|
||||
network_id: Optional[int] = None,
|
||||
status: Optional[IPStatus] = None,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
search: Optional[str] = None
|
||||
) -> tuple[int, List[IPAddressModel]]:
|
||||
"""获取IP列表"""
|
||||
query = db.query(IPAddressModel)
|
||||
|
||||
if network_id:
|
||||
query = query.filter(IPAddressModel.network_id == network_id)
|
||||
|
||||
if status:
|
||||
query = query.filter(IPAddressModel.status == status)
|
||||
|
||||
if search:
|
||||
search_pattern = f"%{search}%"
|
||||
query = query.filter(
|
||||
(IPAddressModel.ip_address.like(search_pattern)) |
|
||||
(IPAddressModel.mac_address.like(search_pattern)) |
|
||||
(IPAddressModel.hostname.like(search_pattern)) |
|
||||
(IPAddressModel.owner.like(search_pattern))
|
||||
)
|
||||
|
||||
total = query.count()
|
||||
items = query.order_by(IPAddressModel.ip_address).offset(skip).limit(limit).all()
|
||||
|
||||
return total, items
|
||||
|
||||
@staticmethod
|
||||
def create(db: Session, ip_in: IPAddressCreate) -> IPAddressModel:
|
||||
"""手动创建IP(通常由网段创建自动创建)"""
|
||||
db_ip = IPAddressModel(**ip_in.model_dump())
|
||||
db.add(db_ip)
|
||||
db.commit()
|
||||
db.refresh(db_ip)
|
||||
return db_ip
|
||||
|
||||
@staticmethod
|
||||
def update(db: Session, ip_id: int, ip_in: IPAddressUpdate) -> Optional[IPAddressModel]:
|
||||
"""更新IP信息"""
|
||||
db_ip = IPService.get_by_id(db, ip_id)
|
||||
if not db_ip:
|
||||
return None
|
||||
|
||||
update_data = ip_in.model_dump(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(db_ip, field, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_ip)
|
||||
return db_ip
|
||||
|
||||
@staticmethod
|
||||
def update_status(db: Session, ip_address: str, status: IPStatus,
|
||||
mac_address: Optional[str] = None,
|
||||
hostname: Optional[str] = None) -> Optional[IPAddressModel]:
|
||||
"""更新IP状态和扫描信息"""
|
||||
db_ip = IPService.get_by_ip(db, ip_address)
|
||||
if not db_ip:
|
||||
return None
|
||||
|
||||
db_ip.status = status
|
||||
db_ip.last_seen = datetime.utcnow()
|
||||
|
||||
if mac_address:
|
||||
db_ip.mac_address = mac_address
|
||||
|
||||
if hostname:
|
||||
db_ip.hostname = hostname
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_ip)
|
||||
return db_ip
|
||||
|
||||
@staticmethod
|
||||
def set_reserved(db: Session, ip_id: int, reserved: bool = True) -> Optional[IPAddressModel]:
|
||||
"""设置/取消保留IP"""
|
||||
db_ip = IPService.get_by_id(db, ip_id)
|
||||
if not db_ip:
|
||||
return None
|
||||
|
||||
db_ip.status = IPStatus.RESERVED if reserved else IPStatus.AVAILABLE
|
||||
db.commit()
|
||||
db.refresh(db_ip)
|
||||
return db_ip
|
||||
|
||||
@staticmethod
|
||||
def bulk_update_status(db: Session, results: List[Dict[str, Any]]):
|
||||
"""批量更新IP状态
|
||||
|
||||
Args:
|
||||
results: 扫描结果列表,每个元素包含 ip_address, status, mac_address, hostname
|
||||
"""
|
||||
for result in results:
|
||||
ip_address = result.get('ip_address')
|
||||
status = result.get('status')
|
||||
mac_address = result.get('mac_address')
|
||||
hostname = result.get('hostname')
|
||||
|
||||
db_ip = IPService.get_by_ip(db, ip_address)
|
||||
if db_ip:
|
||||
db_ip.status = status
|
||||
db_ip.last_seen = datetime.utcnow()
|
||||
if mac_address:
|
||||
db_ip.mac_address = mac_address
|
||||
if hostname:
|
||||
db_ip.hostname = hostname
|
||||
|
||||
db.commit()
|
||||
|
||||
@staticmethod
|
||||
def get_by_network(db: Session, network_id: int) -> List[IPAddressModel]:
|
||||
"""获取指定网段下的所有IP"""
|
||||
return db.query(IPAddressModel).filter(
|
||||
IPAddressModel.network_id == network_id
|
||||
).order_by(IPAddressModel.ip_address).all()
|
||||
Reference in New Issue
Block a user