Files
ipam/backend/app/tasks/scan_tasks.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

216 lines
7.2 KiB
Python

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
}