diff --git a/src/backend/app/api/network_config.py b/src/backend/app/api/network_config.py index b512273..6c618df 100644 --- a/src/backend/app/api/network_config.py +++ b/src/backend/app/api/network_config.py @@ -1,5 +1,4 @@ import asyncio -import logging import telnetlib3 from pathlib import Path from typing import Dict, List, Optional @@ -41,8 +40,6 @@ class SwitchConfigurator: password: str = None, timeout: int = None, max_workers: int = 5, - ensp_mode: bool = False, - ensp_port: int = 2000, ensp_command_delay: float = 0.5, **ssh_options ): @@ -52,34 +49,45 @@ class SwitchConfigurator: self.semaphore = asyncio.Semaphore(max_workers) self.backup_dir = Path("config_backups") self.backup_dir.mkdir(exist_ok=True) - self.ensp_mode = ensp_mode - self.ensp_port = ensp_port self.ensp_delay = ensp_command_delay self.ssh_options = ssh_options async def _get_or_create_connection(self, ip: str): - """ - 从连接池获取连接,如果没有则新建 Telnet 连接 - """ if ip in self.connection_pool: logger.debug(f"复用已有连接: {ip}") return self.connection_pool[ip] logger.info(f"建立新连接: {ip}") - reader, writer = await telnetlib3.open_connection(host=ip, port=23) + reader, writer = await telnetlib3.open_connection( + host=ip, + port=23, + encoding=None, + shell=None + ) try: - if self.username != 'NONE' : - await asyncio.wait_for(reader.readuntil(b"Username:"), timeout=self.timeout) - writer.write(f"{self.username}\n") + if self.username and self.username.upper() != "NONE": + try: + logger.debug("等待用户名提示...") + await asyncio.wait_for(reader.readuntil(b"Username:"), timeout=self.timeout) + writer.write(self.username.encode() + b"\n") + await writer.drain() + logger.info("用户名发送完成") + except asyncio.TimeoutError: + logger.warning("未收到用户名提示,可能交换机不要求用户名") - await asyncio.wait_for(reader.readuntil(b"Password:"), timeout=self.timeout) - writer.write(f"{self.password}\n") + try: + logger.debug("等待密码提示...") + await asyncio.wait_for(reader.readuntil(b"assword:"), timeout=self.timeout) + writer.write(self.password.encode() + b"\n") + await writer.drain() + logger.info("密码发送完成") + except asyncio.TimeoutError: + writer.close() + raise EnspConnectionException("未收到密码提示,登录失败") await asyncio.sleep(1) - except asyncio.TimeoutError: - writer.close() - raise EnspConnectionException("登录超时,未收到用户名或密码提示") + except Exception as e: writer.close() raise EnspConnectionException(f"登录异常: {e}") @@ -88,9 +96,6 @@ class SwitchConfigurator: return reader, writer async def _send_ensp_commands(self, ip: str, commands: List[str]) -> bool: - """ - 通过 Telnet 协议发送命令 - """ try: reader, writer = await self._get_or_create_connection(ip) @@ -98,10 +103,24 @@ class SwitchConfigurator: if cmd.startswith("!"): logger.debug(f"跳过特殊命令: {cmd}") continue + logger.info(f"[{ip}] 发送命令: {cmd}") - writer.write(f"{cmd}\n") + writer.write(cmd.encode() + b"\n") await writer.drain() await asyncio.sleep(self.ensp_delay) + try: + output = b"" + while True: + chunk = await asyncio.wait_for(reader.read(1024), timeout=1) + if not chunk: + break + output += chunk + if output: + logger.info(f"[{ip}] 返回结果:\n{output.decode(errors='ignore').strip()}") + else: + logger.warning(f"[{ip}] 返回为空") + except asyncio.TimeoutError: + logger.warning(f"[{ip}] 读取返回超时 (命令: {cmd})") logger.info(f"[{ip}] 所有命令发送完成") return True @@ -114,9 +133,5 @@ class SwitchConfigurator: return False async def execute_raw_commands(self, ip: str, commands: List[str]) -> bool: - """ - 对外接口:单台交换机执行命令 - """ async with self.semaphore: - success = await self._send_ensp_commands(ip, commands) - return success + return await self._send_ensp_commands(ip, commands)