From 495ec0df40f306fa466cc3c389b17e2aaa4df053 Mon Sep 17 00:00:00 2001 From: 3 Date: Mon, 26 May 2025 18:29:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=EF=BC=88=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E6=9C=AA=E8=B0=83=E7=94=A8=E5=BA=93error)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/Dockerfile/Dockerfile | 26 ++++++++++ src/backend/app/api/command_parser.py | 25 ++++++++++ src/backend/app/api/init.py | 7 ++- src/backend/app/api/network_config.py | 63 +++++++++++++++++++++++++ src/backend/app/init.py | 7 ++- src/backend/app/services/ai_services.py | 61 ++++++++++++++++++++++++ src/backend/exceptions.py | 11 +++++ src/backend/run.py | 6 +++ 8 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 src/backend/Dockerfile/Dockerfile create mode 100644 src/backend/app/services/ai_services.py create mode 100644 src/backend/exceptions.py diff --git a/src/backend/Dockerfile/Dockerfile b/src/backend/Dockerfile/Dockerfile new file mode 100644 index 0000000..4b83782 --- /dev/null +++ b/src/backend/Dockerfile/Dockerfile @@ -0,0 +1,26 @@ +FROM python:3.13.2-slim + +WORKDIR /app + +# 安装系统依赖 +RUN apt-get update && \ + apt-get install -y --no-install-recommends gcc python3-dev libffi-dev && \ + rm -rf /var/lib/apt/lists/* + +# 安装Python依赖 +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# 复制应用代码 +COPY . . + +# 设置环境变量 +ENV FLASK_APP=run.py +ENV FLASK_ENV=production +ENV PYTHONPATH=/app + +# 暴露端口 +EXPOSE 5000 + +# 启动命令 +CMD ["gunicorn", "--bind", "0.0.0.0:5000", "run:app"] \ No newline at end of file diff --git a/src/backend/app/api/command_parser.py b/src/backend/app/api/command_parser.py index b299629..8aa3ab7 100644 --- a/src/backend/app/api/command_parser.py +++ b/src/backend/app/api/command_parser.py @@ -1 +1,26 @@ # 解析中文命令逻辑 +from flask import request, jsonify +from ...services.ai_service import get_network_config +from ...exceptions import InvalidInputError + + +@api_blueprint.route('/parse_command', methods=['POST']) +def parse_command_handler(): + """处理自然语言命令解析""" + try: + data = request.get_json() + if not data or 'command' not in data: + raise InvalidInputError("缺少命令参数") + + command = data['command'] + config = get_network_config(command) + + return jsonify({ + "status": "success", + "config": config + }) + + except InvalidInputError as e: + return jsonify({"status": "error", "message": str(e)}), 400 + except Exception as e: + return jsonify({"status": "error", "message": f"服务器内部错误: {str(e)}"}), 500 \ No newline at end of file diff --git a/src/backend/app/api/init.py b/src/backend/app/api/init.py index 766baef..527ee6a 100644 --- a/src/backend/app/api/init.py +++ b/src/backend/app/api/init.py @@ -1 +1,6 @@ -# 注册api蓝图 \ No newline at end of file +# 注册api蓝图 +from flask import Blueprint + +api_blueprint = Blueprint('api', __name__) + +from . import command_parser, network_config \ No newline at end of file diff --git a/src/backend/app/api/network_config.py b/src/backend/app/api/network_config.py index 12e329e..bf518d9 100644 --- a/src/backend/app/api/network_config.py +++ b/src/backend/app/api/network_config.py @@ -1 +1,64 @@ #配置生成和交换机交互逻辑 +from flask import request, jsonify +from netmiko import ConnectHandler +from ...config import Config +from ...exceptions import NetworkDeviceError + + +@api_blueprint.route('/apply_config', methods=['POST']) +def apply_config_handler(): + """应用配置到网络设备""" + try: + data = request.get_json() + required_fields = ['config', 'device_ip'] + if not all(field in data for field in required_fields): + raise InvalidInputError("缺少必要参数") + + # 安全验证 + validate_configuration(data['config']) + + # 执行配置 + output = execute_switch_config( + device_ip=data['device_ip'], + commands=data['config'], + username=Config.SWITCH_USERNAME, + password=Config.SWITCH_PASSWORD + ) + + return jsonify({ + "status": "success", + "output": output + }) + + except NetworkDeviceError as e: + return jsonify({"status": "error", "message": str(e)}), 502 + except Exception as e: + return jsonify({"status": "error", "message": str(e)}), 500 + + +def validate_configuration(commands): + """配置安全验证""" + dangerous_commands = ['delete', 'erase', 'format', 'reload'] + for cmd in dangerous_commands: + if any(cmd in line.lower() for line in commands): + raise InvalidInputError(f"检测到危险命令: {cmd}") + + +def execute_switch_config(device_ip, commands, username, password): + """执行交换机配置""" + device = { + 'device_type': 'cisco_ios', + 'host': device_ip, + 'username': username, + 'password': password, + 'timeout': 10 + } + + try: + with ConnectHandler(**device) as conn: + conn.enable() # 进入特权模式 + output = conn.send_config_set(commands) + conn.save_config() # 保存配置 + return output + except Exception as e: + raise NetworkDeviceError(f"设备配置失败: {str(e)}") \ No newline at end of file diff --git a/src/backend/app/init.py b/src/backend/app/init.py index c5df723..5872ee6 100644 --- a/src/backend/app/init.py +++ b/src/backend/app/init.py @@ -1,12 +1,17 @@ + from flask import Flask -from .api import api_blueprint +from flask_cors import CORS def create_app(): app = Flask(__name__) app.config.from_object('config.Config') + # 启用CORS(开发环境) + CORS(app, resources={r"/api/*": {"origins": "*"}}) + # 注册蓝图 + from .api import api_blueprint app.register_blueprint(api_blueprint, url_prefix='/api') return app \ No newline at end of file diff --git a/src/backend/app/services/ai_services.py b/src/backend/app/services/ai_services.py new file mode 100644 index 0000000..e1b2600 --- /dev/null +++ b/src/backend/app/services/ai_services.py @@ -0,0 +1,61 @@ +# 调用api +import requests +import json +from ...config import Config +from ...exceptions import AIServiceError + + +def get_network_config(command: str) -> list: + """调用AI服务生成配置""" + headers = { + "Authorization": f"Bearer {Config.AI_API_KEY}", + "Content-Type": "application/json" + } + + payload = { + "command": command, + "vendor": "cisco", + "strict_mode": True + } + + try: + response = requests.post( + Config.AI_API_ENDPOINT, + headers=headers, + json=payload, + timeout=8 + ) + response.raise_for_status() + + result = response.json() + if not result.get('success'): + raise AIServiceError(result.get('message', 'AI服务返回错误')) + + return result['config'] + + except requests.exceptions.Timeout: + raise AIServiceError("AI服务响应超时") + except requests.exceptions.RequestException as e: + raise AIServiceError(f"API请求失败: {str(e)}") + + +# --------------- backend/config.py --------------- +import os +from dotenv import load_dotenv + +load_dotenv() + + +class Config: + # 应用配置 + ENV = os.getenv("FLASK_ENV", "production") + SECRET_KEY = os.getenv("SECRET_KEY", "supersecretkey") + + # AI服务配置 + AI_API_KEY = os.getenv("AI_API_KEY", "") + AI_API_ENDPOINT = os.getenv("AI_API_ENDPOINT", "https://api.siliconflow.ai/v1/network") + + # 网络设备配置 + SWITCH_USERNAME = os.getenv("SWITCH_USER", "admin") + SWITCH_PASSWORD = os.getenv("SWITCH_PASS", "Cisco123!") + DEFAULT_DEVICE_IP = os.getenv("DEFAULT_DEVICE_IP", "192.168.1.1") \ No newline at end of file diff --git a/src/backend/exceptions.py b/src/backend/exceptions.py new file mode 100644 index 0000000..a4d1b58 --- /dev/null +++ b/src/backend/exceptions.py @@ -0,0 +1,11 @@ +class InvalidInputError(Exception): + """输入验证异常""" + pass + +class NetworkDeviceError(Exception): + """网络设备通信异常""" + pass + +class AIServiceError(Exception): + """AI服务异常""" + pass diff --git a/src/backend/run.py b/src/backend/run.py index e69de29..6a943c3 100644 --- a/src/backend/run.py +++ b/src/backend/run.py @@ -0,0 +1,6 @@ +from app import create_app + +app = create_app() + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=5000)