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,4 @@
|
||||
# app/tasks/__init__.py
|
||||
from app.tasks.celery_app import celery_app
|
||||
|
||||
__all__ = ['celery_app']
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,55 @@
|
||||
from celery import Celery
|
||||
from celery.schedules import crontab
|
||||
from app.core.config import settings
|
||||
|
||||
# 创建 Celery 应用
|
||||
celery_app = Celery(
|
||||
'ipam_tasks',
|
||||
broker=settings.CELERY_BROKER_URL,
|
||||
backend=settings.CELERY_RESULT_BACKEND
|
||||
)
|
||||
|
||||
# 配置
|
||||
celery_app.conf.update(
|
||||
task_serializer='json',
|
||||
accept_content=['json'],
|
||||
result_serializer='json',
|
||||
timezone='Asia/Shanghai',
|
||||
enable_utc=True,
|
||||
task_track_started=True,
|
||||
task_time_limit=30 * 60, # 任务超时30分钟
|
||||
worker_prefetch_multiplier=1,
|
||||
worker_max_tasks_per_child=1000,
|
||||
)
|
||||
|
||||
# 必须在配置后导入并注册任务
|
||||
from app.tasks.scan_tasks import register_tasks
|
||||
_tasks = register_tasks(celery_app)
|
||||
|
||||
# 导出任务函数
|
||||
scan_network_task = _tasks['scan_network_task']
|
||||
scan_single_ip_task = _tasks['scan_single_ip_task']
|
||||
full_network_scan = _tasks['full_network_scan']
|
||||
update_all_statistics = _tasks['update_all_statistics']
|
||||
quick_status_update = _tasks['quick_status_update']
|
||||
|
||||
# 定时任务配置
|
||||
celery_app.conf.beat_schedule = {
|
||||
# 每小时执行一次全量扫描
|
||||
'full-scan-every-hour': {
|
||||
'task': 'app.tasks.scan_tasks.full_network_scan',
|
||||
'schedule': 3600.0, # 每小时
|
||||
'args': (True, True, True) # ping, arp, dns
|
||||
},
|
||||
# 每天凌晨2点执行一次完整扫描
|
||||
'full-scan-daily': {
|
||||
'task': 'app.tasks.scan_tasks.full_network_scan',
|
||||
'schedule': crontab(hour=2, minute=0),
|
||||
'args': (True, True, True)
|
||||
},
|
||||
# 每15分钟更新一次统计
|
||||
'update-stats-every-15min': {
|
||||
'task': 'app.tasks.scan_tasks.update_all_statistics',
|
||||
'schedule': 900.0, # 15分钟
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
from typing import List, Dict, Any, Optional
|
||||
from celery import Task
|
||||
import logging
|
||||
|
||||
from app.core.database import SessionLocal
|
||||
from app.services.network_service import NetworkService
|
||||
from app.services.enhanced_scan_service import EnhancedScanService
|
||||
from app.services.scan_service import ScanService
|
||||
from app.models.network import TaskStatus, ScanTask, IPAddress
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def register_tasks(celery_app):
|
||||
"""
|
||||
注册任务到 Celery 应用
|
||||
"""
|
||||
|
||||
@celery_app.task(bind=True)
|
||||
def scan_network_task(self, network_id: int,
|
||||
enable_ping: bool = True,
|
||||
enable_arp: bool = True,
|
||||
enable_dns: bool = True,
|
||||
timeout: int = 2):
|
||||
"""
|
||||
Celery 异步任务:扫描指定网段
|
||||
"""
|
||||
task_id = self.request.id
|
||||
db = SessionLocal()
|
||||
|
||||
try:
|
||||
# 获取网段信息
|
||||
network = NetworkService.get_by_id(db, network_id)
|
||||
if not network:
|
||||
logger.error(f"网段 {network_id} 不存在")
|
||||
return {'status': 'failed', 'error': '网段不存在'}
|
||||
|
||||
logger.info(f"开始扫描网段: {network.cidr}")
|
||||
|
||||
# 更新任务状态为运行中
|
||||
# 查找已存在的任务记录
|
||||
db_task = db.query(ScanTask).filter(ScanTask.celery_task_id == task_id).first()
|
||||
if not db_task:
|
||||
db_task = ScanService.create_scan_task(db, 'ping', network_id)
|
||||
db_task.celery_task_id = task_id
|
||||
db.commit()
|
||||
db.refresh(db_task)
|
||||
|
||||
ScanService.update_task_status(db, db_task.id, TaskStatus.RUNNING, progress=0)
|
||||
|
||||
# 执行扫描
|
||||
results = EnhancedScanService.scan_network(
|
||||
network.cidr,
|
||||
enable_ping=enable_ping,
|
||||
enable_arp=enable_arp,
|
||||
enable_dns=enable_dns,
|
||||
timeout=timeout
|
||||
)
|
||||
|
||||
# 批量更新IP信息
|
||||
stats = EnhancedScanService.bulk_update_ips_from_scan(db, results)
|
||||
|
||||
# 更新任务状态
|
||||
ScanService.update_task_status(
|
||||
db,
|
||||
db_task.id,
|
||||
TaskStatus.COMPLETED,
|
||||
progress=100,
|
||||
total_count=stats['total_scanned'],
|
||||
success_count=stats['online_count']
|
||||
)
|
||||
|
||||
logger.info(f"网段 {network.cidr} 扫描完成: 在线 {stats['online_count']} 台, "
|
||||
f"发现MAC {stats['mac_found_count']} 个, 主机名 {stats['hostname_found_count']} 个")
|
||||
|
||||
return {
|
||||
'status': 'completed',
|
||||
'network_id': network_id,
|
||||
'cidr': network.cidr,
|
||||
'statistics': stats
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"扫描失败: {str(e)}", exc_info=True)
|
||||
if 'db_task' in locals() and db_task:
|
||||
ScanService.update_task_status(
|
||||
db,
|
||||
db_task.id,
|
||||
TaskStatus.FAILED,
|
||||
error_message=str(e)
|
||||
)
|
||||
raise
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@celery_app.task(bind=True)
|
||||
def scan_single_ip_task(self, ip_address: str,
|
||||
enable_ping: bool = True,
|
||||
enable_arp: bool = True,
|
||||
enable_dns: bool = True):
|
||||
"""
|
||||
Celery 异步任务:扫描单个IP
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
result = EnhancedScanService.comprehensive_scan(
|
||||
ip_address,
|
||||
enable_ping=enable_ping,
|
||||
enable_arp=enable_arp,
|
||||
enable_dns=enable_dns
|
||||
)
|
||||
|
||||
# 更新IP信息
|
||||
EnhancedScanService.update_ip_from_scan_result(db, result)
|
||||
db.commit()
|
||||
return result
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@celery_app.task(bind=True)
|
||||
def full_network_scan(self, enable_ping: bool = True,
|
||||
enable_arp: bool = True,
|
||||
enable_dns: bool = True):
|
||||
"""
|
||||
Celery 定时任务:扫描所有网段
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
_, all_networks = NetworkService.get_list(db, limit=1000)
|
||||
|
||||
total_stats = {
|
||||
'total_networks': len(all_networks),
|
||||
'total_ips': 0,
|
||||
'online_count': 0,
|
||||
'mac_found_count': 0,
|
||||
'hostname_found_count': 0
|
||||
}
|
||||
|
||||
logger.info(f"开始全量扫描: {len(all_networks)} 个网段")
|
||||
|
||||
for network in all_networks:
|
||||
try:
|
||||
results = EnhancedScanService.scan_network(
|
||||
network.cidr,
|
||||
enable_ping=enable_ping,
|
||||
enable_arp=enable_arp,
|
||||
enable_dns=enable_dns
|
||||
)
|
||||
|
||||
stats = EnhancedScanService.bulk_update_ips_from_scan(db, results)
|
||||
|
||||
total_stats['total_ips'] += stats['total_scanned']
|
||||
total_stats['online_count'] += stats['online_count']
|
||||
total_stats['mac_found_count'] += stats['mac_found_count']
|
||||
total_stats['hostname_found_count'] += stats['hostname_found_count']
|
||||
|
||||
logger.info(f"网段 {network.cidr} 扫描完成: 在线 {stats['online_count']} 台")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"网段 {network.cidr} 扫描失败: {str(e)}")
|
||||
|
||||
logger.info(f"全量扫描完成: {total_stats}")
|
||||
|
||||
return {
|
||||
'status': 'completed',
|
||||
'statistics': total_stats
|
||||
}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@celery_app.task(bind=True)
|
||||
def update_all_statistics(self):
|
||||
"""
|
||||
定时任务:更新所有网段统计信息
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
NetworkService.get_all_stats(db)
|
||||
logger.info("所有网段统计信息已更新")
|
||||
return {'status': 'completed'}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@celery_app.task(bind=True)
|
||||
def quick_status_update(self, ip_addresses: List[str]):
|
||||
"""
|
||||
快速更新多个IP的状态(用于实时扫描)
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
results = []
|
||||
|
||||
for ip in ip_addresses:
|
||||
result = EnhancedScanService.comprehensive_scan(ip, enable_ping=True, enable_arp=True, enable_dns=False)
|
||||
EnhancedScanService.update_ip_from_scan_result(db, result)
|
||||
results.append(result)
|
||||
|
||||
db.commit()
|
||||
|
||||
return {
|
||||
'scanned_count': len(ip_addresses),
|
||||
'online_count': sum(1 for r in results if r.get('success')),
|
||||
'results': results
|
||||
}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
# 返回任务函数供外部使用
|
||||
return {
|
||||
'scan_network_task': scan_network_task,
|
||||
'scan_single_ip_task': scan_single_ip_task,
|
||||
'full_network_scan': full_network_scan,
|
||||
'update_all_statistics': update_all_statistics,
|
||||
'quick_status_update': quick_status_update
|
||||
}
|
||||
Reference in New Issue
Block a user