from typing import List, Dict, Any, Optional from sqlalchemy.orm import Session from datetime import datetime import asyncio import subprocess import ipaddress from app.models.network import ScanTask, Network, IPAddress from app.models.network import TaskStatus, TaskType, IPStatus from app.core.config import settings class ScanService: """扫描服务""" @staticmethod def ping_single_ip(ip_address: str, timeout: int = 2) -> Dict[str, Any]: """ 对单个IP执行Ping扫描 """ try: result = subprocess.run( ['ping', '-c', '1', '-W', str(timeout), ip_address], capture_output=True, text=True ) is_alive = result.returncode == 0 return { 'ip_address': ip_address, 'status': 'online' if is_alive else 'offline', 'response_time': None, 'success': is_alive } except Exception as e: return { 'ip_address': ip_address, 'status': 'error', 'error': str(e), 'success': False } @staticmethod def ping_network(cidr: str) -> List[Dict[str, Any]]: """ 对整个网段执行Ping扫描(同步版本) """ network = ipaddress.ip_network(cidr, strict=False) ips = [str(ip) for ip in network.hosts()] results = [] for ip in ips: result = ScanService.ping_single_ip(ip, settings.PING_TIMEOUT) results.append(result) return results @staticmethod def create_scan_task(db: Session, task_type: TaskType, network_id: Optional[int] = None) -> ScanTask: """ 创建扫描任务 """ task = ScanTask( task_type=task_type, network_id=network_id, status=TaskStatus.PENDING, progress=0 ) db.add(task) db.commit() db.refresh(task) return task @staticmethod def update_task_status(db: Session, task_id: int, status: TaskStatus, progress: int = None, total_count: int = None, success_count: int = None, failed_count: int = None, error_message: str = None): """ 更新任务状态 """ task = db.query(ScanTask).filter(ScanTask.id == task_id).first() if not task: return task.status = status if progress is not None: task.progress = progress if total_count is not None: task.total_count = total_count if success_count is not None: task.success_count = success_count if failed_count is not None: task.failed_count = failed_count if error_message: task.error_message = error_message if status == TaskStatus.RUNNING and task.started_at is None: task.started_at = datetime.utcnow() elif status in [TaskStatus.COMPLETED, TaskStatus.FAILED]: task.completed_at = datetime.utcnow() db.commit() @staticmethod def get_task_by_id(db: Session, task_id: int) -> Optional[ScanTask]: """ 获取任务信息 """ return db.query(ScanTask).filter(ScanTask.id == task_id).first() @staticmethod def get_task_list(db: Session, skip: int = 0, limit: int = 50) -> tuple[int, List[ScanTask]]: """ 获取任务列表 """ query = db.query(ScanTask) total = query.count() items = query.order_by(ScanTask.id.desc()).offset(skip).limit(limit).all() return total, items @staticmethod def update_ip_status_from_scan_results(db: Session, results: List[Dict[str, Any]]): """ 根据扫描结果更新IP状态 """ online_count = 0 offline_count = 0 for result in results: ip_address = result.get('ip_address') is_online = result.get('success', False) db_ip = db.query(IPAddress).filter(IPAddress.ip_address == ip_address).first() if db_ip: if is_online: db_ip.status = IPStatus.ONLINE db_ip.last_seen = datetime.utcnow() online_count += 1 elif db_ip.status == IPStatus.ONLINE: # 曾经在线,现在不在线,标记为离线 db_ip.status = IPStatus.OFFLINE offline_count += 1 db.commit() return online_count, offline_count