通过Paramiko在Cisco交换机执行命令——优化动态等待性能
解决Cisco设备SSH执行命令的动态等待问题
我太懂这个痛点了!之前用paramiko的exec_command没法批量执行命令,换成invoke_shell()后用固定time.sleep()又总踩坑——要么命令还没执行完就提前收输出,要么明明几秒就能跑完的命令要等半天。其实核心思路是通过识别Cisco设备的提示符来判断命令是否执行完成,而不是靠固定时长等待,下面给你两种靠谱的实现方案:
方案一:基于设备提示符的动态等待(最可靠)
Cisco设备执行完任何命令后,都会回到对应的提示符(比如用户模式的Switch#、配置模式的Switch(config)#),我们可以监听输出内容,直到提示符出现就停止等待,同时加上超时机制防止无限挂起。
代码示例
import paramiko import time def run_cisco_command(ssh_client, cmd, timeout=30): # 创建交互通道 channel = ssh_client.invoke_shell() # 先关闭分页,避免输出被截断在--More-- channel.send("terminal length 0\n") time.sleep(0.5) # 短等待确保分页命令生效 # 获取当前设备的基础提示符(比如Switch#) initial_output = channel.recv(65535).decode() base_prompt = initial_output.strip().split("\n")[-1] # 发送目标命令 channel.send(f"{cmd}\n") full_output = "" start_time = time.time() while True: # 检查是否有新输出 if channel.recv_ready(): full_output += channel.recv(65535).decode() # 检测到基础提示符,说明命令执行完成 if base_prompt in full_output: break # 超时判断,避免无限等待 if time.time() - start_time > timeout: raise TimeoutError(f"命令执行超时,已超过{timeout}秒") # 短间隔轮询,比固定sleep灵活 time.sleep(0.1) # 清理输出:去掉命令本身、分页指令和最后的提示符 cleaned_output = full_output.replace("terminal length 0\n", "")\ .replace(f"{cmd}\n", "")\ .replace(base_prompt, "")\ .strip() return cleaned_output # 使用示例 if __name__ == "__main__": ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: ssh.connect("192.168.1.1", username="admin", password="your_password") # 执行ARP查询命令 arp_result = run_cisco_command(ssh, "show arp") print(arp_result) # 执行耗时较长的命令,比如show tech-support tech_result = run_cisco_command(ssh, "show tech-support", timeout=120) print(tech_result) finally: ssh.close()
关键说明
- 关闭分页:必须先执行
terminal length 0,否则输出会卡在--More--,永远等不到提示符。 - 识别提示符:通过初始输出获取设备的基础提示符,确保后续能准确判断命令执行完成的状态。
- 超时机制:设置最大等待时间,避免因设备卡死或网络问题导致程序无限挂起。
方案二:处理配置类命令的进阶动态等待
如果要执行端口配置这类需要切换模式的命令,需要针对不同模式的提示符做判断,比如进入配置模式后等待Switch(config)#,进入接口模式后等待Switch(config-if)#。
代码示例
def configure_cisco_interface(ssh_client, interface_name, ip_addr): channel = ssh_client.invoke_shell() channel.send("terminal length 0\n") time.sleep(0.5) # 获取用户模式提示符 user_output = channel.recv(65535).decode() user_prompt = user_output.strip().split("\n")[-1] config_prompt = user_prompt.replace("#", "(config)#") if_prompt = config_prompt.replace("(config)", "(config-if)") # 进入全局配置模式 channel.send("configure terminal\n") start_time = time.time() while config_prompt not in channel.recv(65535).decode(): if time.time() - start_time > 10: raise TimeoutError("进入配置模式超时") time.sleep(0.1) # 进入目标接口 channel.send(f"interface {interface_name}\n") start_time = time.time() while if_prompt not in channel.recv(65535).decode(): if time.time() - start_time > 10: raise TimeoutError(f"进入接口{interface_name}超时") time.sleep(0.1) # 发送配置命令 channel.send(f"ip address {ip_addr}\n") channel.send("no shutdown\n") # 退出接口和配置模式 channel.send("exit\n") channel.send("exit\n") # 等待回到用户模式 start_time = time.time() while user_prompt not in channel.recv(65535).decode(): if time.time() - start_time > 10: raise TimeoutError("退出配置模式超时") time.sleep(0.1) return f"接口{interface_name}配置完成"
为什么不推荐固定sleep?
固定时长等待完全依赖经验值,遇到网络波动、设备负载高的情况就会失效:
- 若sleep时间太短:命令还在执行就提前收输出,导致结果不完整。
- 若sleep时间太长:明明几秒能完成的命令要等很久,浪费时间。
而基于提示符的动态等待完全贴合设备的实际执行状态,既灵活又可靠。
内容的提问来源于stack exchange,提问作者Lele
相关产品推荐
相关产品推荐

