This commit is contained in:
Jerry 2025-08-30 15:36:38 +08:00
parent 8f5ac21be1
commit f80fc9fc56

View File

@ -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' :
if self.username and self.username.upper() != "NONE":
try:
logger.debug("等待用户名提示...")
await asyncio.wait_for(reader.readuntil(b"Username:"), timeout=self.timeout)
writer.write(f"{self.username}\n")
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")
await asyncio.sleep(1)
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("登录超时,未收到用户名或密码提示")
raise EnspConnectionException("未收到密码提示,登录失败")
await asyncio.sleep(1)
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)