import socket from fastapi import (APIRouter, HTTPException, Response) from typing import List, Optional from pydantic import BaseModel import asyncio import psutil import ipaddress import time from datetime import datetime from ..models.requests import CLICommandRequest, ConfigRequest, TrafficQueryRequest from ...app.services.ai_services import AIService from ...app.api.network_config import SwitchConfigurator from ...config import settings from ..services.network_scanner import NetworkScanner from ..services.traffic_monitor import traffic_monitor from ..utils.logger import logger router = APIRouter(prefix="", tags=["API"]) scanner = NetworkScanner() @router.get("/", include_in_schema=False) async def root(): return { "message": "欢迎使用AI交换机配置系统", "docs": f"{settings.API_PREFIX}/docs", "redoc": f"{settings.API_PREFIX}/redoc", "endpoints": [ "/parse_command", "/apply_config", "/scan_network", "/list_devices", "/batch_apply_config", "/traffic/realtime", "/traffic/cache/clear" ] } @router.get("/favicon.ico", include_in_schema=False) async def favicon(): return Response(status_code=204) class BatchConfigRequest(BaseModel): config: dict switch_ips: List[str] username: str = None password: str = None timeout: int = None @router.get("/test") async def test_endpoint(): return {"message": "Hello World"} @router.get("/scan_network", summary="扫描网络中的交换机") async def scan_network(subnet: str = "192.168.1.0/24"): try: devices = await asyncio.to_thread(scanner.scan_subnet, subnet) return { "success": True, "devices": devices, "count": len(devices) } except Exception as e: raise HTTPException(500, f"扫描失败: {str(e)}") @router.get("/list_devices", summary="列出已发现的交换机") async def list_devices(): return { "devices": await asyncio.to_thread(scanner.load_cached_devices) } class DeviceItem(BaseModel): name: str ip: str vendor: str class CommandRequest(BaseModel): command: str devices: List[DeviceItem] @router.post("/parse_command", response_model=dict) async def parse_command(request: CommandRequest): """解析中文命令并返回每台设备的配置 JSON""" missing_vendor = [d for d in request.devices if not d.vendor or d.vendor.strip() == ""] if missing_vendor: names = ", ".join([d.name for d in missing_vendor]) raise HTTPException( status_code=400, detail=f"以下设备未配置厂商: {names}" ) try: ai_service = AIService(settings.SILICONFLOW_API_KEY, settings.SILICONFLOW_API_URL) config = await ai_service.parse_command(request.command, [d.dict() for d in request.devices]) return {"success": True, "config": config.get("results", [])} except Exception as e: raise HTTPException( status_code=400, detail=f"Failed to parse command: {str(e)}" ) @router.post("/apply_config", response_model=dict) async def apply_config(request: ConfigRequest): """应用配置到交换机""" try: configurator = SwitchConfigurator( username=request.username, password=request.password, timeout=request.timeout, vendor=request.vendor ) result = await configurator.safe_apply(request.switch_ip, request.config) return {"success": True, "result": result} except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to apply config: {str(e)}" ) @router.post("/execute_cli_commands", response_model=dict) async def execute_cli_commands(request: CLICommandRequest): """执行前端生成的CLI命令""" try: username, password = request.extract_credentials() configurator = SwitchConfigurator( username=username, password=password, timeout=settings.SWITCH_TIMEOUT, ) result = await configurator.execute_raw_commands( ip=request.switch_ip, commands=request.commands ) return { "success": True, "output": result, } except Exception as e: raise HTTPException(500, detail=str(e)) @router.get("/network_adapters", summary="获取网络适配器网段") async def get_network_adapters(): try: def sync_get_adapters(): net_if_addrs = psutil.net_if_addrs() networks = [] for interface, addrs in net_if_addrs.items(): for addr in addrs: if addr.family == socket.AF_INET: ip = addr.address netmask = addr.netmask network = ipaddress.IPv4Network(f"{ip}/{netmask}", strict=False) networks.append({ "adapter": interface, "network": str(network), "ip": ip, "subnet_mask": netmask }) return networks networks = await asyncio.to_thread(sync_get_adapters) return {"networks": networks} except Exception as e: return {"error": f"获取网络适配器信息失败: {str(e)}"} @router.post("/traffic/realtime", summary="查询交换机接口实时流量") async def get_realtime_traffic(request: TrafficQueryRequest): """ 查询交换机接口实时流量速率(Kbps) - 支持多个接口同时查询 - 首次查询速率返回 0 - 单个接口查询失败不影响其他接口 """ # 提取认证信息 username = request.username or settings.SWITCH_USERNAME password = request.password or settings.SWITCH_PASSWORD # 创建配置器(复用连接池) configurator = SwitchConfigurator( username=username, password=password, timeout=settings.SWITCH_TIMEOUT ) results = [] current_time = time.time() # 遍历所有接口 for interface in request.interfaces: interface_data = { "interface": interface, "status": "unknown", "in_speed_kbps": 0.0, "out_speed_kbps": 0.0, "in_bytes": 0, "out_bytes": 0, "error": None } try: # 获取查询命令 command = traffic_monitor.get_query_command(request.vendor, interface) if not command: interface_data["error"] = f"不支持的厂商: {request.vendor}" results.append(interface_data) continue # 执行查询命令 try: output = await configurator.execute_raw_commands( ip=request.switch_ip, commands=[command] ) # 解析输出 stats = traffic_monitor.parse_interface_stats(request.vendor, str(output)) if stats is None: interface_data["error"] = "解析接口统计失败" results.append(interface_data) continue in_bytes, out_bytes, status = stats # 计算速率 in_speed_kbps, out_speed_kbps = traffic_monitor.calculate_speed( request.switch_ip, interface, in_bytes, out_bytes, current_time ) # 更新结果 interface_data.update({ "status": status, "in_speed_kbps": round(in_speed_kbps, 2), "out_speed_kbps": round(out_speed_kbps, 2), "in_bytes": in_bytes, "out_bytes": out_bytes }) except Exception as e: interface_data["error"] = f"查询失败: {str(e)}" logger.error(f"查询接口 {interface} 失败: {e}") except Exception as e: interface_data["error"] = f"未知错误: {str(e)}" logger.error(f"处理接口 {interface} 时发生异常: {e}", exc_info=True) results.append(interface_data) return { "success": True, "switch_ip": request.switch_ip, "vendor": request.vendor, "timestamp": datetime.utcnow().isoformat() + "Z", "data": results } @router.delete("/traffic/cache/clear", summary="清除流量监控缓存") async def clear_traffic_cache(switch_ip: Optional[str] = None): """ 清除流量监控缓存 - 不指定 switch_ip: 清除所有缓存 - 指定 switch_ip: 只清除该交换机的缓存 """ try: count = traffic_monitor.clear_cache(switch_ip) return { "success": True, "message": f"已清除 {count} 条缓存记录", "switch_ip": switch_ip or "all" } except Exception as e: raise HTTPException(500, f"清除缓存失败: {str(e)}") @router.get("/traffic/cache/stats", summary="获取缓存统计信息") async def get_cache_stats(): """获取流量监控缓存统计信息""" try: stats = traffic_monitor.get_cache_stats() return { "success": True, "stats": stats } except Exception as e: raise HTTPException(500, f"获取缓存统计失败: {str(e)}")