Python学习:Paramiko库简介
一、Paramiko库简介
Paramiko 是一个Python环境下实现SSHv2操作的模块
支持以口令认证和公钥认证的远程连接
可执行远程命令,安全文件传输,端口转发与隧道
支持Linux、Windows、MacOS等系统,安全性高,功能全面
实现运维自动化的一个工具
二、Paramiko组件
协议类:
Channel
Client
Message
Packetizer
Transport
以上几个类是Paramiko的核心组件,下面对Channel和SSHClient进行一些说明:
1、Channel 类
一个基于SSH传输的安全隧道。Channel 的行为类似于套接字,其API应与Python的套接字API无异。
主要用于创建在 SSH Transport 上的安全通道。
由于SSHv2采用基于窗口的流量控制,如果停止从 Channel 接收数据,且其缓冲区已满,服务器将无法继续发送数据,直接读取部分数据为止,但不会影响同一传输上的其他通道。如果服务器未读取接收到的数据,那么调用 send 方法可能会阻塞,除非设置了超时。
(根据Paramiko官方文档翻译整理,如有疏漏敬请指正!)
Channel的方法有很多,对于我来说常用的也就两、三个:
send()
发送数据
recv(nbytes)
接收数据
nbytes:一次接收的最大数量
close()
关闭通道
2、Client类
SSHClient
与SSH服务器之间会话的高级表示。封装了 Transport、Channel、SFTPClient。负责处理身份验证和打开通道的大部分工作
(1)密钥相关方法:
| 方法 | 说明 |
| load_system_host_keys() | 以只读的方式加载密钥 |
| load_host_keys() | 以读写的加载密钥 |
| save_host_keys() | 将密钥保存至文件 |
| get_host_keys() | 加载本地的密钥,用于检查或修改本地密钥 |
| set_missing_host_key_policy() |
设备在连接没有已知密钥的服务器时所使用的策略: RejectPolicy:默认策略,当密钥不在已知列表中时,直接拒绝连接,并抛出SSHException AutoAddPolicy:自动将未知服务器的密钥添加到本地密钥集合中,然后继续连接 WarningPolicy:当遇到未知服务器密钥时,记录告警日志,接受密钥并继续连接 |
Python 内部维护一个密钥集合。load_host_keys()会把密钥加载到这个集合中,并且这些密钥被认为是可写的。
当修改这个集合再调用save_host_keys(),Paramiko会把整个集合写入指定文件中。
load_system_host_keys()加载的密钥为只读,save_host_keys()不会将密钥写回到文件中,能够使用系统已知的密钥进行验证,又能单独管理自己的密钥文件
(2)connect()
用于连接到SSH服务器并进行身份验证。
connect(
hostname, # 要连接的 SSH 服务器的主机名或IP地址,必填
port=22, # SSH 服务器的端口号,选填,默认: 22,如果修改了端口则必填
username=None, # 用户认证的用户名
password=None, # 用户认证的密码
pkey=None, # 用于认证的私钥对象,如RSAKey,与key_filename互斥
key_filename=None, # 私钥文件的路径
timeout=None, # TCP 连接超时时间。控制建立 TCP 连接时最长等待时间
allow_agent=True, # 是否允许 使用运行中的 SSH agent 进行认证,默认为:True
look_for_keys=True, # 是否在 ~/.ssh/ 目录下搜索私钥文件,默认为:True
compress=False, # 是否启用压缩,默认:False
sock=None, # 一个已打开的套接字,用于替代直接创建新的TCP连接。可用于通过代理或已建立的隧道连接
banner_timeout=None, # 等待服务器返回 banner 的超时时间,默认与 timeout 相同
auth_timeout=None, # 认证阶段的超时时间,如果认证过程超时,则终止连接
channel_timeout=None, # 通道操作的超时时间。
passphrase=None, # 用于解密私钥文件的口令,如果密钥加密且未提供 passphrase,则提示输入
disabled_algorithms=None, # 用于禁用某些加密算法、密钥交换算法、MAC算法等。
transport_factory=None, # 用于自定义 Transport 类的实例
auth_strategy=None # 自定义认证策略。如不提供则按默认顺序尝试认证方式
)
所有参数中,常用的不多:hostname、port、usname、password、pkey或key_filename、auto_timeout、timeout、look_for_keys
示例:
# 这是一段来自于 Paramiko 官方文档的一个示例
# 创建一个 SSHClient 实例
client = SSHClient()
# 从系统文件中加载主机密钥文件,连接服务器时,Paramiko可以自动验证服务器的主机密钥是否与本地记录匹配
client.load_system_host_keys()
# 连接到指定的 SSH 服务器
client.connect('ssh.example.com')
# 在远程服务器上执行命令:ls -l
stdin, stdout, stderr = client.exec_command('ls -l')
(3)close()
关闭当前使用的SSHClient连接
(4)exec_command()
作用:在远程服务器上执行一条命令,并获取到这条命令的输入、输出和挺不错信息。
远程Linux服务器时推荐使用。
exec_command(
command, # 要执行的命令
bufszie=-1, # 读写数据时的缓冲区大小。一般不作修改,默认即可
timeout=None, # 设置命令所在通道的超时时间,即等待命令执行完成的时间
get_pty=False, # 向服务器请求一个伪终端,默认不请求
environment=None # 一个设置了 shell 环境变量的字典,将被合并到远程命令执行的默认环境中
)
每条 exec_command() 都是独立的 shell 会话,每次只能执行一条命令,执行完命令后通道自动关闭。如果需要执行一连串的命令,可使用 && 或写成一个脚本。
get_pty=False:某些交互命令(sudo、top、password)需要终端环境才可以运行。普通命令不需要终端(ls、cat)。
exec_command()的返回值是一个三元组(stdin,stdout,stderr)
stdin:可以往里面写数据【.write()】,这些数据会作为命令的标准输入
stdout:命令的正常输出结果,可以用 .read() 或 .readlines() 读取
stderr:命令的错误输出
(5)invoke_shell()
作用:在 SSH 服务器上启动一个交互式 Shell 会话。会话会打开一个新的通道,并使用指定的终端类型和大小将其连接到一个伪终端。
远程网络设备时推荐使用。
invoke_shell(
term='vt100', # 要模拟的终端类型
width=80, # 终端窗口的宽度,以字符数为单位
heigth=24, # 终端窗口的高度,以字符数为单位
width_pixels=0, # 终端窗口的宽度,以像素为单位
height_pixels=0, # 终端窗口的高度,以像素为单位
environment=None # 命令的环境变量
)
invoke_shell()和exec_command()的不同之处在于,invoke_shell()打开一个可持续交互的终端,可连接发送多条命令,并实时读取输出。
三、单台设备连接
下面通过一个小实验来熟悉Paramiko的使用。
1、一个简单的拓扑

AR1路由器通过G0/0/0接口连接主机,并配置接口IP地址,同时配置SSH,在这里我使用的是AAA 用户名和密码的方式进行验证。配置好后记得测试一下。确保SSH能正常登录。
通过Paramiko模块SSH连接路由器,并给路由器配置一个 lookback 0 接口,IP地址为:10.1.1.1/32
2、脚本内容
接下来创建一个 .py 的脚本文件,脚本内容如下:
# 1、导入所需的模块
import paramiko
import time
# 创建一个 SSHClient 实例:ssh_client
ssh_client = paramiko.SSHClient()
# 设置缺失主机密钥的策略为:AutoAddPolicy()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 发起SSH连接
ssh_client.connect(hostname='192.168.44.100', username='admin', password='admin@123', look_for_keys=False)
# 调用invoke_shell() 方法请求远程服务器分配一个交互式 Shell 会话
command = ssh_client.invoke_shell()
time.sleep(2)
# 使用通道的 send() 方法向远程 Shell 发送字符串指令
command.send("display ip interface brief\n")
time.sleep(1)
command.send("system-view\n")
command.send("interface loopback 0\n")
command.send("ip address 10.1.1.1 32\n")
command.send("display ip interface brief\n")
time.sleep(2)
# 通过 recv() 将返回的结果赋值给 result
result = command.recv(65535).decode("ASCII")
print(result)
# 关闭通道
command.close()
# 关闭SSHClient
ssh_client.close()
为什么会用到 time 模块?
在有些情况下(如网络延迟,设备对指令的执行等)需要一定的时间才能返回结果。同时send()发送的指令是执行完一条后接着继续执行下一条,直到所有send()全部发送完。像“display ip interface brief”指令需要等待一会儿才会显示出结果。send()本身没有等待时间,revc()可能会捕获不到回显结果。
解决办法就是使用 time 模块的sleep()来进行硬性的时间等待。
3、实验结果



第一张图片显示的结果为没有使用sleep()捕获的结果。第二张图片为在connect()和发送第一条指令“display ip interface brief\n”处设置sleep()后的结果。第三张图片为在send()发送最后一条指令后添加sleep()后的结果。
具体在哪条send()指令发送后需要使用sleep()需要根据实际情况来进行调整。
同时会发现在脚本里send()每次发送的字符串指令后面都有一个换行符 \n。这是因为如果只是send('display ip interface brief')这样发送指令的话,它是没有执行的,就像我们平时通过超级终端连接上设备进行调试时一样。
四、多台设备连接
如果我们同时要对多台设备进行日常巡检需要如何实现?
我们可以为每台设备创建一个脚本文件,然后分别执行。但这种方式肯定不是最便捷的。
整个网络中同一类型的设备基本上会使用相同的登录方式、账号、密码,唯一变动的是每台设备的IP地址不一样。那么就可以根据这个特点,将所有设备的IP地址定义为一个List列表,对List列表进行循环逐一获取设备的IP地址,然后对每台设备进行远程操作。
1、实验拓扑
3台路由器已配置好,均可通过SSH登录。
需要远程连接这3台设备,然后执行'display ip interface brief’查看一些信息

2、脚本
# 1、导入所需的模块
import paramiko
import time
ip_list = ['192.168.44.101', '192.168.44.105', '192.168.44.108']
for ip in ip_list:
# 创建一个 SSHClient 实例:ssh_client
ssh_client = paramiko.SSHClient()
# 设置缺失主机密钥的策略为:AutoAddPolicy()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 发起SSH连接
ssh_client.connect(hostname=ip, username='admin', password='admin@123', look_for_keys=False)
# 调用invoke_shell() 方法请求远程服务器分配一个交互式 Shell 会话
command = ssh_client.invoke_shell()
time.sleep(2)
print(f"========== 登录设备" + ip + f" ==========")
# 使用通道的 send() 方法向远程 Shell 发送字符串指令
command.send("display ip interface brief\n")
time.sleep(1)
# 通过 recv() 将返回的结果赋值给 result
result = command.recv(65535).decode("ASCII")
print(result)
# 关闭通道
command.close()
# 关闭SSHClient
ssh_client.close()
3、执行结果

更多推荐
所有评论(0)