mirror of
https://github.com/Jerryplusy/AI-powered-switches.git
synced 2025-07-04 21:29:18 +00:00
114514
This commit is contained in:
parent
b3e6aa7d5c
commit
60359b54ee
@ -19,7 +19,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) -> dict[str, str | list[Any]] | dict[str, str] | None:
|
||||||
"""
|
"""
|
||||||
尝试本地解析常见命令
|
尝试本地解析常见命令
|
||||||
"""
|
"""
|
||||||
@ -32,7 +33,7 @@ class CommandParser:
|
|||||||
if vlan_id:
|
if vlan_id:
|
||||||
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": []
|
||||||
}
|
}
|
||||||
@ -62,7 +63,7 @@ class CommandParser:
|
|||||||
if "vlan" in command:
|
if "vlan" in command:
|
||||||
vlan_id = next((p for p in parts if p.isdigit()), None)
|
vlan_id = next((p for p in parts if p.isdigit()), None)
|
||||||
if vlan_id:
|
if vlan_id:
|
||||||
config["vlan"] = int(vlan_id)
|
config["vlan"] = vlan_id
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ async def batch_apply_config(request: BatchConfigRequest):
|
|||||||
for ip in request.switch_ips:
|
for ip in request.switch_ips:
|
||||||
try:
|
try:
|
||||||
configurator = SwitchConfigurator()
|
configurator = SwitchConfigurator()
|
||||||
results[ip] = await configurator.apply_config(ip, request.config)
|
results[ip] = await configurator.safe_apply(ip, request.config)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
results[ip] = str(e)
|
results[ip] = str(e)
|
||||||
return {"results": results}
|
return {"results": results}
|
||||||
@ -80,7 +80,7 @@ async def apply_config(request: ConfigRequest):
|
|||||||
password=settings.SWITCH_PASSWORD,
|
password=settings.SWITCH_PASSWORD,
|
||||||
timeout=settings.SWITCH_TIMEOUT
|
timeout=settings.SWITCH_TIMEOUT
|
||||||
)
|
)
|
||||||
result = await configurator.apply_config(request.switch_ip, request.config)
|
result = await configurator.safe_apply(request.switch_ip, request.config)
|
||||||
return {"success": True, "result": result}
|
return {"success": True, "result": result}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
@ -64,7 +64,7 @@ class SwitchConfigurator:
|
|||||||
self.ensp_delay = ensp_command_delay
|
self.ensp_delay = ensp_command_delay
|
||||||
self.ssh_options = ssh_options
|
self.ssh_options = ssh_options
|
||||||
|
|
||||||
async def _apply_config(self, ip: str, config: Union[Dict, SwitchConfig]) -> str:
|
async def apply_config(self, ip: str, config: Union[Dict, SwitchConfig]) -> str:
|
||||||
"""实际配置逻辑"""
|
"""实际配置逻辑"""
|
||||||
if isinstance(config, dict):
|
if isinstance(config, dict):
|
||||||
config = SwitchConfig(**config)
|
config = SwitchConfig(**config)
|
||||||
@ -127,6 +127,7 @@ class SwitchConfigurator:
|
|||||||
|
|
||||||
# 关闭连接
|
# 关闭连接
|
||||||
writer.close()
|
writer.close()
|
||||||
|
# noinspection PyBroadException
|
||||||
try:
|
try:
|
||||||
await writer.wait_closed()
|
await writer.wait_closed()
|
||||||
except:
|
except:
|
||||||
@ -253,7 +254,7 @@ class SwitchConfigurator:
|
|||||||
"""安全配置应用(自动回滚)"""
|
"""安全配置应用(自动回滚)"""
|
||||||
backup_path = await self._backup_config(ip)
|
backup_path = await self._backup_config(ip)
|
||||||
try:
|
try:
|
||||||
result = await self._apply_config(ip, config)
|
result = await self.apply_config(ip, config)
|
||||||
if not await self._validate_config(ip, config):
|
if not await self._validate_config(ip, config):
|
||||||
raise SwitchConfigException("配置验证失败")
|
raise SwitchConfigException("配置验证失败")
|
||||||
return {
|
return {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from fastapi import HTTPException, status
|
from fastapi import HTTPException, status
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
class AICommandParseException(HTTPException):
|
class AICommandParseException(HTTPException):
|
||||||
def __init__(self, detail: str):
|
def __init__(self, detail: str):
|
||||||
@ -23,9 +22,10 @@ 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 +34,10 @@ 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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class SiliconFlowAPIException(HTTPException):
|
||||||
|
def __init__(self, detail: str, status_code: int = 400):
|
||||||
|
super().__init__(
|
||||||
|
status_code=status_code,
|
||||||
|
detail=f"SiliconFlow API error: {detail}"
|
||||||
|
)
|
@ -1,33 +1,44 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class InterceptHandler(logging.Handler):
|
class InterceptHandler(logging.Handler):
|
||||||
def emit(self, record):
|
def emit(self, record):
|
||||||
# Get corresponding Loguru level if it exists
|
# 获取对应的Loguru日志级别
|
||||||
try:
|
try:
|
||||||
level = logger.level(record.levelname).name
|
level = logger.level(record.levelname).name
|
||||||
except ValueError:
|
except ValueError:
|
||||||
level = record.levelno
|
level = record.levelno
|
||||||
|
|
||||||
# Find caller from where originated the logged message
|
# 查找日志来源
|
||||||
frame, depth = logging.currentframe(), 2
|
frame, depth = logging.currentframe(), 2
|
||||||
while frame.f_code.co_filename == logging.__file__:
|
while frame and frame.f_code.co_filename == logging.__file__:
|
||||||
frame = frame.f_back
|
frame = frame.f_back
|
||||||
depth += 1
|
depth += 1
|
||||||
|
|
||||||
|
# 使用Loguru记录日志
|
||||||
logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
|
logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
|
||||||
|
|
||||||
|
|
||||||
def setup_logging():
|
def setup_logging():
|
||||||
# 拦截标准logging
|
# 拦截标准logging
|
||||||
logging.basicConfig(handlers=[InterceptHandler()], level=0)
|
logging.basicConfig(handlers=[InterceptHandler()], level=logging.NOTSET)
|
||||||
|
|
||||||
# 配置loguru
|
# 移除所有现有处理器
|
||||||
logger.configure(
|
logger.remove()
|
||||||
handlers=[
|
|
||||||
{"sink": sys.stdout,
|
# 直接添加处理器(避免configure方法)
|
||||||
"format": "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>"}
|
logger.add(
|
||||||
]
|
sys.stdout,
|
||||||
|
format=(
|
||||||
|
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
|
||||||
|
"<level>{level: <8}</level> | "
|
||||||
|
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - "
|
||||||
|
"<level>{message}</level>"
|
||||||
|
),
|
||||||
|
level="DEBUG",
|
||||||
|
enqueue=True
|
||||||
)
|
)
|
Loading…
x
Reference in New Issue
Block a user