Compare commits

...

3 Commits

Author SHA1 Message Date
3
a32d4803b7 Merge remote-tracking branch 'origin/main' 2025-06-16 13:59:15 +08:00
3
46ac2e63b4 基础错误已修复,准备后续的开发 2025-06-16 13:59:04 +08:00
3e8da7c8d2 删掉没用的文件 2025-06-16 13:24:52 +08:00
17 changed files with 91 additions and 25 deletions

View File

@ -3,8 +3,8 @@ from src.backend.app.api.endpoints import router
from src.backend.app.utils.logger import setup_logging from src.backend.app.utils.logger import setup_logging
from src.backend.config import settings from src.backend.config import settings
app = FastAPI() api_app = FastAPI()
app.include_router(router,prefix="/api") api_app.include_router(router,prefix="/api")
def create_app() -> FastAPI: def create_app() -> FastAPI:
# 设置日志 # 设置日志

View File

@ -1,8 +1,7 @@
from typing import Dict, Any from typing import Dict, Any, Optional
from src.backend.app.services.ai_services import AIService from src.backend.app.services.ai_services import AIService
from src.backend.config import settings from src.backend.config import settings
class CommandParser: class CommandParser:
def __init__(self): def __init__(self):
self.ai_service = AIService(settings.SILICONFLOW_API_KEY, settings.SILICONFLOW_API_URL) self.ai_service = AIService(settings.SILICONFLOW_API_KEY, settings.SILICONFLOW_API_URL)
@ -19,7 +18,8 @@ class CommandParser:
# 本地无法解析则调用AI服务 # 本地无法解析则调用AI服务
return await self.ai_service.parse_command(command) return await self.ai_service.parse_command(command)
def _try_local_parse(self, command: str) -> Dict[str, Any]: @staticmethod
def _try_local_parse(command: str) -> Optional[Dict[str, Any]]:
""" """
尝试本地解析常见命令 尝试本地解析常见命令
""" """
@ -28,12 +28,13 @@ class CommandParser:
# VLAN配置 # VLAN配置
if "vlan" in command and "创建" in command: if "vlan" in command and "创建" in command:
parts = command.split() parts = command.split()
vlan_id = next((p for p in parts if p.isdigit()), None) vlan_id_str = next((p for p in parts if p.isdigit()), None)
if vlan_id: if vlan_id_str:
vlan_id = int(vlan_id_str) # 转换为整数
return { return {
"type": "vlan", "type": "vlan",
"vlan_id": int(vlan_id), "vlan_id": vlan_id, # 使用整数
"name": f"VLAN{vlan_id}", "name": f"VLAN{vlan_id}", # 使用转换后的整数
"interfaces": [] "interfaces": []
} }
@ -60,9 +61,9 @@ class CommandParser:
config["description"] = description config["description"] = description
if "vlan" in command: if "vlan" in command:
vlan_id = next((p for p in parts if p.isdigit()), None) vlan_id_str = next((p for p in parts if p.isdigit()), None)
if vlan_id: if vlan_id_str:
config["vlan"] = int(vlan_id) config["vlan"] = int(vlan_id_str) # 转换为整数
return config return config

View File

@ -3,7 +3,7 @@ from typing import List, Dict
from pydantic import BaseModel from pydantic import BaseModel
from ...config import settings from ...config import settings
from ..services.network_scanner import NetworkScanner from ..services.network_scanner import NetworkScanner
from ..api.network_config import SwitchConfigurator, SwitchConfig from ..api.network_config import SwitchConfigurator
router = APIRouter(prefix="/api", tags=["API"]) router = APIRouter(prefix="/api", tags=["API"])
scanner = NetworkScanner() scanner = NetworkScanner()

View File

@ -1,6 +1,7 @@
import asyncio import asyncio
import logging import logging
import telnetlib3 import telnetlib3
import time
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional, Union from typing import Dict, List, Optional, Union
@ -142,6 +143,14 @@ class SwitchConfigurator:
except Exception as e: except Exception as e:
raise EnspConnectionException(f"eNSP连接失败: {str(e)}") raise EnspConnectionException(f"eNSP连接失败: {str(e)}")
async def _clean_idle_connections(self):
"""连接池清理机制"""
now = time.time()
for ip, (conn, last_used) in list(self._connection_pool.items()):
if now - last_used > 300: # 5分钟空闲超时
conn.close()
del self._connection_pool[ip]
async def _send_ssh_commands(self, ip: str, commands: List[str]) -> str: async def _send_ssh_commands(self, ip: str, commands: List[str]) -> str:
"""SSH协议执行""" """SSH协议执行"""
async with self.semaphore: async with self.semaphore:

View File

@ -58,4 +58,7 @@ class AIService:
return json.loads(config_str) return json.loads(config_str)
raise SiliconFlowAPIException("Invalid JSON format returned from AI") raise SiliconFlowAPIException("Invalid JSON format returned from AI")
except httpx.HTTPError as e: except httpx.HTTPError as e:
raise SiliconFlowAPIException(str(e)) raise SiliconFlowAPIException(
detail=f"API请求失败: {str(e)}",
status_code=e.response.status_code if hasattr(e, "response") else 500
)

View File

@ -0,0 +1,36 @@
import networkx as nx
from scipy.optimize import minimize
class NetworkOptimizer:
def __init__(self, devices):
"""基于图论的网络优化模型"""
self.graph = self.build_topology_graph(devices)
def build_topology_graph(self, devices):
"""构建网络拓扑图"""
G = nx.Graph()
for device in devices:
G.add_node(device['ip'], type=device['type'])
# 添加连接关系(示例)
G.add_edge('192.168.1.1', '192.168.1.2', bandwidth=1000)
return G
def optimize_path(self, source, target):
"""计算最优路径"""
return nx.shortest_path(self.graph, source, target)
def bandwidth_optimization(self):
"""带宽优化模型"""
def objective(x):
# 最小化最大链路利用率
return max(x)
constraints = (
{'type': 'eq', 'fun': lambda x: sum(x) - total_bandwidth}
)
result = minimize(objective, initial_guess, constraints=constraints)
return result.x

View File

@ -1,5 +1,14 @@
from fastapi import HTTPException, status from fastapi import HTTPException, status
from typing import Optional
class SiliconFlowAPIException(Exception):
"""硅基流动API异常"""
def __init__(self, detail: str, status_code: int = 500):
self.detail = detail
self.status_code = status_code
super().__init__(detail)
def __str__(self):
return f"SiliconFlowAPI Error [{self.status_code}]: {self.detail}"
class AICommandParseException(HTTPException): class AICommandParseException(HTTPException):
def __init__(self, detail: str): def __init__(self, detail: str):
@ -23,9 +32,9 @@ class ConfigBackupException(SwitchConfigException):
"""配置备份失败异常""" """配置备份失败异常"""
def __init__(self, ip: str): def __init__(self, ip: str):
super().__init__( super().__init__(
detail=f"无法备份设备 {ip} 的配置", detail=f"无法备份设备 {ip} 的配置"
recovery_guide="检查设备存储空间或权限"
) )
self.recovery_guide = "检查设备存储空间或权限" # 在子类中存储恢复指南
class ConfigRollbackException(SwitchConfigException): class ConfigRollbackException(SwitchConfigException):
"""回滚失败异常""" """回滚失败异常"""
@ -34,3 +43,4 @@ class ConfigRollbackException(SwitchConfigException):
detail=f"设备 {ip} 回滚失败(原始错误:{original_error}", detail=f"设备 {ip} 回滚失败(原始错误:{original_error}",
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY status_code=status.HTTP_422_UNPROCESSABLE_ENTITY
) )
self.recovery_guide = "尝试手动恢复配置或重启设备"

View File

@ -24,8 +24,8 @@ class BulkConfigurator:
return "\n".join(r.stdout for r in results) return "\n".join(r.stdout for r in results)
finally: finally:
await self.pool.release_connection(ip, conn) await self.pool.release_connection(ip, conn)
@staticmethod
def _generate_commands(self, config: BulkSwitchConfig) -> List[str]: def _generate_commands(config: BulkSwitchConfig) -> List[str]:
"""命令生成(纯业务逻辑)""" """命令生成(纯业务逻辑)"""
commands = [] commands = []
if config.vlan_id: if config.vlan_id:

View File

@ -1,9 +1,7 @@
import asyncio import asyncio
import time
import asyncssh import asyncssh
from typing import Dict from typing import Dict
class SwitchConnectionPool: class SwitchConnectionPool:
""" """
交换机连接池支持自动重连和负载均衡 交换机连接池支持自动重连和负载均衡
@ -35,10 +33,15 @@ class SwitchConnectionPool:
async def release_connection(self, ip: str, conn: asyncssh.SSHClientConnection): async def release_connection(self, ip: str, conn: asyncssh.SSHClientConnection):
async with self._lock: async with self._lock:
if conn.is_connected() and self._pools[ip].qsize() < self._max_conn: if hasattr(conn, 'is_closed') and not conn.is_closed() and self._pools[ip].qsize() < self._max_conn:
await self._pools[ip].put(conn)
elif hasattr(conn, 'closed') and not conn.closed and self._pools[ip].qsize() < self._max_conn:
await self._pools[ip].put(conn) await self._pools[ip].put(conn)
else: else:
conn.close() try:
conn.close()
except:
pass
async def close_all(self): async def close_all(self):
async with self._lock: async with self._lock:

View File

@ -12,3 +12,8 @@ aiofiles>=24.1.0
telnetlib3>=2.0.4 telnetlib3>=2.0.4
asyncssh>=2.14.0 asyncssh>=2.14.0
aiofiles>=24.1.0 aiofiles>=24.1.0
networkx==3.1
scipy==1.11.1
stable-baselines3==2.0.0
plotly==5.15.0
pandas==2.0.3

Binary file not shown.

Before

Width:  |  Height:  |  Size: 869 KiB

View File

@ -1 +0,0 @@
# 论文(markdown形式)