4. Paramiko实战:SSH连接与命令执行

说实话,做网络运维这么多年,我最怕的就是半夜被叫起来,一台一台设备登录上去敲命令。尤其是几十台交换机要改个VLAN配置,那真是噩梦。后来我接触了Paramiko,才算是真正解放了双手。

Paramiko是Python里最成熟的SSH库之一。它封装了SSHv2协议,让我们可以用代码直接连设备、发命令、收结果。说白了,它就是帮你自动完成了「打开SecureCRT → 输入IP → 输入密码 → 敲命令 → 看输出」这一整套动作。

核心思路: 用Python代码替代人工SSH操作,实现网络设备的远程管理和自动化运维。

4.1 Paramiko核心API

Paramiko的API其实不复杂,核心就几个类。我刚开始学的时候,觉得文档写得有点绕,后来自己总结了一下,其实就三样东西:

  • SSHClient:客户端对象,负责建立连接
  • connect():连接方法,传IP、用户名、密码进去
  • exec_command():执行命令的方法

嗯,这里要注意,Paramiko默认不支持交互式命令(比如进入配置模式后还要输yes/no那种)。它更适合执行一次性命令,比如show running-configdisplay 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分钟。

注意: exec_command()执行完命令后,连接不会自动关闭。记得调用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'])
)
避坑指南: 我曾经在批量执行时没做异常处理,结果第15台设备连接超时,整个脚本崩溃退出。前面14台的结果全白干了。从那以后,我每条命令都包在try-except里,并且把成功和失败的结果分别记录到不同文件。

完整的异常处理模板,我一般是这么写的:

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%的异常场景。你拿去改改就能用。

Paramiko SSH自动化执行流程 开始 创建SSHClient对象 set_missing_host_key_policy() connect() 连接设备 exec_command() 执行命令 异常处理 try-except AuthenticationException SSHException socket.timeout close() 关闭连接

这张图把整个流程串起来了。从创建客户端到关闭连接,每一步都有对应的API。异常处理贯穿始终,就像安全网一样兜住各种意外。

小技巧: 如果你要频繁执行命令,可以考虑复用同一个SSH连接,而不是每次执行都重新连接。这样可以大幅提升效率。我有个脚本就是保持长连接,每5分钟采集一次设备状态,跑了半年都没断过。

好了,Paramiko的核心用法就这些。说白了就是三步:连上去、发命令、收结果。但真正用好它,需要你对异常处理、连接复用、结果解析这些细节有足够的重视。毕竟,自动化运维追求的不是「能跑」,而是「稳定地跑」。

专注资料整理