mirror of
https://github.com/Jerryplusy/AI-powered-switches.git
synced 2025-07-04 05:09:19 +00:00
Compare commits
3 Commits
5c6e89b937
...
a32d4803b7
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a32d4803b7 | ||
![]() |
46ac2e63b4 | ||
3e8da7c8d2 |
@ -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:
|
||||||
# 设置日志
|
# 设置日志
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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:
|
||||||
|
0
src/backend/app/experiments/network_experiment.py
Normal file
0
src/backend/app/experiments/network_experiment.py
Normal file
0
src/backend/app/experiments/performance_analysis.py
Normal file
0
src/backend/app/experiments/performance_analysis.py
Normal file
0
src/backend/app/services/ai_configurator.py
Normal file
0
src/backend/app/services/ai_configurator.py
Normal 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
|
||||||
|
)
|
36
src/backend/app/services/network_optimizer.py
Normal file
36
src/backend/app/services/network_optimizer.py
Normal 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
|
@ -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 = "尝试手动恢复配置或重启设备"
|
0
src/backend/app/utils/visualization/topology_3d.py
Normal file
0
src/backend/app/utils/visualization/topology_3d.py
Normal 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:
|
||||||
|
@ -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:
|
||||||
|
@ -11,4 +11,9 @@ typing-extensions>=4.0.0
|
|||||||
aiofiles>=24.1.0
|
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 |
@ -1 +0,0 @@
|
|||||||
# 论文(markdown形式)
|
|
Loading…
x
Reference in New Issue
Block a user