4. Paramiko实战:SSH连接与命令执行
说实话,做网络运维这么多年,我最怕的就是半夜被叫起来,一台一台设备登录上去敲命令。尤其是几十台交换机要改个VLAN配置,那真是噩梦。后来我接触了Paramiko,才算是真正解放了双手。
Paramiko是Python里最成熟的SSH库之一。它封装了SSHv2协议,让我们可以用代码直接连设备、发命令、收结果。说白了,它就是帮你自动完成了「打开SecureCRT → 输入IP → 输入密码 → 敲命令 → 看输出」这一整套动作。
4.1 Paramiko核心API
Paramiko的API其实不复杂,核心就几个类。我刚开始学的时候,觉得文档写得有点绕,后来自己总结了一下,其实就三样东西:
- SSHClient:客户端对象,负责建立连接
- connect():连接方法,传IP、用户名、密码进去
- exec_command():执行命令的方法
嗯,这里要注意,Paramiko默认不支持交互式命令(比如进入配置模式后还要输yes/no那种)。它更适合执行一次性命令,比如show running-config、display current-configuration这种。
我个人习惯把连接参数单独拎出来,写成字典,方便复用:
import paramiko
device = {
'hostname': '192.168.1.1',
'port': 22,
'username': 'admin',
'password': 'cisco123'
}
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(**device)
这里有个小坑——set_missing_host_key_policy。如果不加这行,第一次连陌生设备时会报错。我刚开始踩过这个坑,排查了半天才发现是host key的问题。
WarningPolicy,既不会报错中断,又能看到警告日志。AutoAddPolicy虽然方便,但安全性稍差。
4.2 执行单条命令
连接建立后,执行命令就很简单了。核心方法是exec_command(),它会返回三个文件对象:stdin、stdout、stderr。
stdin, stdout, stderr = client.exec_command('display version')
output = stdout.read().decode('utf-8')
print(output)
为什么要有stdin?因为有些命令需要交互输入,比如修改密码时。不过大多数情况下,我们只用stdout读结果就够了。
我记得有一次,客户让我批量采集200台交换机的序列号。用这个方式,写个循环,每台设备连上去执行display device manuinfo,然后解析输出里的序列号字段。原来人工要干两天的活,脚本跑完不到10分钟。
client.close()释放资源,否则连接数多了会出问题。
4.3 批量执行命令
批量执行有两种场景:一种是同一台设备执行多条命令,另一种是多台设备执行同一条命令。
先看第一种,同一台设备多条命令:
commands = [
'system-view',
'vlan 100',
'description Management_VLAN',
'quit',
'save'
]
for cmd in commands:
stdin, stdout, stderr = client.exec_command(cmd)
print(f"执行: {cmd}")
print(stdout.read().decode())
第二种,多台设备同一条命令,其实就是套个循环:
devices = ['192.168.1.1', '192.168.1.2', '192.168.1.3']
command = 'display clock'
for ip in devices:
try:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(ip, username='admin', password='cisco123')
stdin, stdout, stderr = client.exec_command(command)
result = stdout.read().decode()
print(f"{ip} 的时间: {result.strip()}")
client.close()
except Exception as e:
print(f"{ip} 连接失败: {str(e)}")
你想想看,如果公司有50台核心交换机,要统一校时。用这个脚本,几秒钟就搞定了。我以前手动一台台对时间,经常对到一半就忘了哪台改过哪台没改。
4.4 异常处理
网络设备运维,最怕的就是脚本跑一半挂了。连接超时、认证失败、命令不存在……各种情况都可能发生。所以异常处理一定要做扎实。
我总结了几种常见异常和对应的处理方式:
| 异常类型 | 常见原因 | 处理建议 |
|---|---|---|
| AuthenticationException | 用户名或密码错误 | 记录日志,跳过该设备,继续下一台 |
| SSHException | SSH协议版本不兼容 | 检查设备SSH配置,尝试降级加密算法 |
| socket.timeout | 网络不通或设备繁忙 | 增加重试机制,最多重试3次 |
| CommandNotFound | 命令不存在或权限不足 | 检查命令拼写,确认用户权限 |
我曾经遇到过一个问题:某台老设备SSH版本太低,Paramiko默认连不上。排查了半天,发现是disabled_algorithms参数没配。加上之后就好了:
client.connect(
hostname=ip,
username='admin',
password='cisco123',
disabled_algorithms=dict(pub_keys=['rsa-sha2-512', 'rsa-sha2-256'])
)
完整的异常处理模板,我一般是这么写的:
import paramiko
import time
from paramiko.ssh_exception import AuthenticationException, SSHException
def ssh_execute(ip, username, password, command, retries=3):
for attempt in range(retries):
try:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(ip, username=username, password=password, timeout=10)
stdin, stdout, stderr = client.exec_command(command)
result = stdout.read().decode()
client.close()
return result
except AuthenticationException:
return f"认证失败: {ip}"
except SSHException as e:
if attempt < retries - 1:
time.sleep(2)
continue
return f"SSH异常: {ip} - {str(e)}"
except Exception as e:
if attempt < retries - 1:
time.sleep(2)
continue
return f"未知错误: {ip} - {str(e)}"
这个模板我用了好几年,基本覆盖了90%的异常场景。你拿去改改就能用。
这张图把整个流程串起来了。从创建客户端到关闭连接,每一步都有对应的API。异常处理贯穿始终,就像安全网一样兜住各种意外。
好了,Paramiko的核心用法就这些。说白了就是三步:连上去、发命令、收结果。但真正用好它,需要你对异常处理、连接复用、结果解析这些细节有足够的重视。毕竟,自动化运维追求的不是「能跑」,而是「稳定地跑」。