干了这么多年网络运维,最让人头疼的从来不是技术难题,而是那些机械重复的"体力活"。新上线30台交换机要改管理地址、40台路由器要批量下发ntp配置、月底要统计全网设备的软件版本和序列号——每一台都要ssh登录、敲命令、等回显、记录结果、退出,再登录下一台。运气好碰上设备密码没改还能顺畅点,运气不好遇到几台登录超时,一上午就搭进去了。
paramiko这个python库,就是专门解决这个问题的。它是python语言实现的sshv2协议客户端库,说白了就是让你不用手动打开终端,直接用代码去登录路由器、交换机,然后自动执行命令、读取回显、判断结果。配合多线程或异步机制,原来需要半个上午的人工巡检,压缩到几分钟跑完脚本,而且不会漏设备、不会抄错输出。这篇文章我把自己实际使用paramiko做批量查询、批量配置下发、以及踩过的那些坑完整梳理一遍,适合刚接触网络自动化的运维工程师和网络管理员参考,也适合那些想用python把手从重复劳动里解放出来的朋友。
1. 为什么网络设备批量管理非得上paramiko
1.1 日常运维里那些"想死"的重复操作
先具体说说我们平时遇到的场景。公司办公网有80多台接入交换机,分布在各个楼层和分支机构。每季度要做一次配置备份,传统做法是:打开crt或者xshell,输ip、输账号、输密码,进去之后执行display current-configuration(华为)或者show running-config(思科),看到一堆输出,复制、粘贴到文本文件里,命名成以设备名+日期的格式,存到备份目录。80台设备,就算每台3分钟,全程不走神也得4个小时。中间接个电话、被叫去处理个故障,回来就得记着刚才备份到哪一台了。
还有业务变更场景。比如全网要统一修改snmp的只读团体名,或者统一添加上级网管服务器的地址。这种操作特别机械:登录设备、进入系统视图、敲两三条命令、保存配置、退出。但一旦涉及几十台设备,重复劳动的量就非常可观,而且人一疲劳就容易出错——漏一台、命令敲错一个单词、在错误的设备上执行了正确的命令,这些风险远比"不会配"更可怕。
paramiko解决的就是这个层面的问题:把"登录、执行、读取、保存"这套 动作代码化、批量化和可重复化。脚本跑一次,结果自动汇总成表格,哪台成功哪台失败一目了然,既节省时间又降低人为失误率。
1.2 paramiko与其他自动化方案的定位差异
很多人会问:现在不是有ansible、nornir这些自动化工具吗?为什么还要自己写paramiko脚本?
我的看法是,paramiko是所有网络自动化的"底层地基"。ansible的网络模块底层本质上也依赖ssh连接,只不过封装了更多现成的模块和playbook语法。paramiko则是最直接的ssh操作库,你用它写脚本,对"连接、认证、执行、读取"整个过程有完全的控制权,适合处理那些工具封装好的逻辑覆盖不了的场景。
做个直白的类比:ansible这类工具像自动挡汽车,上手快、操作简单,但遇到特殊情况你可能搞不清它内部怎么换挡的;paramiko像手动挡,一切都自己控制,离合油门自己配合,虽然麻烦点,但你能精确知道每个时刻车辆在什么状态。
在实际工作中,我通常用paramiko处理以下场景:
- 批量巡检设备状态(cpu、内存、温度、光功率)
- 批量备份配置文件,并按设备名归档
- 批量下发标准化配置(ntp、snmp、aaa、日志服务器)
- 批量修改口令、批量重启端口、批量清理mac表项
- 对接运维平台,把设备操作封装成可调用的api
如果你只需要偶尔登录一两台设备看看状态,那完全没必要用paramiko,手动敲命令更快。但当设备数量上到几十上百台,或者操作内容需要标准化执行时,paramiko脚本的价值就完全体现出来了。
2. 练手环境怎么搭:模拟器选型与python环境准备
2.1 用gns3还是eve-ng搭实验环境
学paramiko批量管理,总不可能直接拿生产环境练手。我建议用模拟器搭一套练习环境,既有真实设备的命令行体验,又不会因为误操作把线上业务搞挂。
我常用的是gns3,配合思科ios镜像跑路由器,也可以加思科交换机的ios镜像。gns3对paramiko脚本编写来说有个天然优势:它本身就支持在拓扑中给设备分配管理ip,你可以把几台路由器和交换机通过云桥接或者管理交换机构成一个管理网段,宿主机直接ssh访问这些设备的管理地址。
如果你手头主要接触华为设备,也可以使用ensp,虽然ensp的设备仿真类型有限,但跑基础的vrp命令、练习ssh登录足够了。用ensp时要注意一个问题:很多版本的ensp设备默认不开启ssh服务,需要先在设备上配置vty用户、认证方式和rsa密钥。这部分配置本身就是网络基本功,正好一起练了。
我用gns3搭建的练习环境长这样:三台思科路由器(比如7200系列镜像)和两台思科交换机(ios的l2/l3交换机镜像),分别設定管理ip为192.168.56.101到192.168.56.105,掩码24位,网关指向192.168.56.1(宿主机的virtualbox host-only网卡地址)。在路由器上开启ssh服务,配置本地用户和aaa认证,然后从宿主机用命令行验证能正常ssh登录,再接下去写paramiko脚本。
如果你不用模拟器,家里或办公室有几台真实的老旧交换机、路由器,比如思科2960、华为s5700这类的二手设备,闲鱼一两百块一台,拿来练手也很靠谱。真实设备的好处是能遇到各种奇奇怪怪的问题——密钥交换算法不匹配、加密算法太弱导致连接失败、设备时间不对导致认证失败等等,这些在模拟器里往往碰不到。
2.2 python环境与paramiko安装的那些细节
python环境的准备,我推荐直接用python 3.8以上的版本。windows、macos、linux都能跑,paramiko是纯python写的,底层依赖cryptography和bcrypt,这两个包在三大平台都有预编译的wheel包,安装基本不会出问题。
安装paramiko的方式很简单:
pip install paramiko
如果你使用的是国内网络,可能需要换用国内镜像源安装会更快:
pip install paramiko -i https://pypi.tuna.tsinghua.edu.cn/simple
安装完成后,可以用一行代码验证是否安装成功:
import paramiko print(paramiko.__version__)
能正常打印出版本号,就说明环境ok了。
这里有个特别容易被忽略的点:paramiko对python版本有最低要求。新版本的paramiko要求python 3.6以上,如果你还在用python 2.7,paramiko老版本(比如2.x)还能跑,但建议尽早迁移。我在生产环境里见过一台老旧的centos 6服务器上跑python 2.7,用paramiko 2.0连接设备时对某些新设备支持的加密算法不够导致握手失败,后来升级到python 3才解决。
另外一个实用建议:建议用虚拟环境管理项目的第三方库。我习惯给每个自动化项目单独建一个virtualenv或者conda环境,避免不同项目的依赖冲突:
python -m venv netauto_env # windows激活 netauto_env\scripts\activate # linux/macos激活 source netauto_env/bin/activate pip install paramiko
3. 批量查询的核心实现:从单台ssh到并发采集
3.1 单台设备信息抓取的完整代码
不管你要批量操作多少台设备,第一步永远是先打通"单台设备"的ssh连接和命令执行。这一步搞明白了,后面加循环、加并发都只是体力活。
下面是我最常用的一个基础版(伪)代码,作用是ssh登录一台设备,执行命令,打印回显:
import paramiko
import time
def exec_command(host, username, password, command, enable_password=none):
"""ssh登录设备并执行单条命令,返回回显内容"""
# 创建ssh客户端对象
client = paramiko.sshclient()
# 自动添加主机密钥,生产环境建议用load_system_host_keys或已知密钥
client.set_missing_host_key_policy(paramiko.autoaddpolicy())
try:
# 建立连接,timeout控制tcp连接超时时间
client.connect(
hostname=host,
port=22,
username=username,
password=password,
timeout=10,
look_for_keys=false,
allow_agent=false,
banner_timeout=15,
auth_timeout=15
)
# 获取shell通道
shell = client.invoke_shell()
# 等待初始提示符出现
time.sleep(1)
shell.recv(65535)
# 执行命令
shell.send(command + "\n")
time.sleep(2)
output = shell.recv(65535).decode('utf-8', errors='ignore')
return output
finally:
client.close()
if __name__ == '__main__':
output = exec_command(
host='192.168.56.101',
username='admin',
password='admin123',
command='show version'
)
print(output)
这段代码有几点要说明:
invoke_shell()获取的是交互式shell,不是直接执行单条命令。好处是能模拟真实终端登录效果,能处理需要交互的命令(比如进入特权模式后需要输enable密码);坏处是回显内容需要自己根据提示符判断命令是否执行完毕,相对麻烦一些。shell.recv(65535)是一次性读取缓冲区里的数据,但设备执行命令的速度不一样,命令输出多的时候一次读不完。我在演示代码里用sleep(2)等待2秒再读,这是一种偷懒的做法。更严谨的做法是循环读取,直到输出中不再变化,或者检测到命令结束提示符(比如">"或"#")再停下来。timeout=10是tcp连接的超时时间,banner_timeout和auth_timeout分别是ssh协议握手阶段和认证阶段的超时时间。这三个超时时间非常关键,特别是批量跑的时候,如果某个ip不通或者设备ssh服务不正常,没有超时控制的话,脚本会卡在连接阶段很久。
3.2 用户认证的两种方式与安全建议
paramiko支持两种主要认证方式:密码认证和密钥认证。
密码认证最简单直接,脚本里直接把用户名密码传给 connect() 方法。但密码认证的安全问题很明显:密码硬编码在脚本里,一旦脚本泄露,所有设备的管理密码就都暴露了;另外如果频繁用同一密码连接大量设备,在一些有账号锁定策略的设备上可能触发锁定。
密钥认证相对更安全,操作方式是先在运维跳板机上生成一对rsa密钥,然后把公钥分发到网络设备的指定用户下。设备端配置好之后,paramiko连接时会自动用本地私钥进行认证:
client = paramiko.sshclient()
client.connect(
hostname=host,
port=22,
username='admin',
key_filename='/home/ops/.ssh/id_rsa',
timeout=10
)
密钥认证在批量操作中的优势非常明显:脚本里不用放密码,即使脚本被同事看到了,没有私钥文件也登录不了设备。而且密钥认证在批量执行时速度比密码认证快,因为少了密码加密传输的过程。
但在实际网络设备上,密钥认证的配置比服务器复杂。思科设备要在配置里指定 ip ssh pubkey-chain ,华为设备要用 rsa peer-public-key 命令导入公钥,不同厂商不同型号命令格式还有差异。所以很多运维团队实际还是优先用密码认证,靠vault这类密钥管理工具来管理密码。
在这点上我的建议是:如果设备数量不多、操作不频繁,密码认证配合环境变量或配置文件读取密码就够了;如果是大规模的自动化平台,建议还是想办法用密钥认证或者对接专门的密钥管理系统,否则密码轮换一次,你所有脚本里的密码都得跟着改一遍,非常被动。
3.3 并发批量采集的实现与性能对比
单台设备跑通之后,批量就很简单了:读取设备清单列表,循环调用单台执行函数。但要注意的是,循环串行执行有个性能瓶颈——假设每台设备连接加执行命令耗时3秒,30台设备就是90秒,虽然比手工快多了,但还有优化空间。
用python的 concurrent.futures 线程池可以轻松实现并发执行:
import paramiko
import time
from concurrent.futures import threadpoolexecutor, as_completed
def collect_device_info(device):
"""采集单台设备的信息"""
host = device['host']
username = device['username']
password = device['password']
command = device.get('command', 'show version')
try:
client = paramiko.sshclient()
client.set_missing_host_key_policy(paramiko.autoaddpolicy())
client.connect(
hostname=host,
port=22,
username=username,
password=password,
timeout=10,
look_for_keys=false,
allow_agent=false,
banner_timeout=15,
auth_timeout=15
)
shell = client.invoke_shell()
time.sleep(1)
shell.recv(65535)
shell.send(command + "\n")
time.sleep(2)
output = shell.recv(65535).decode('utf-8', errors='ignore')
client.close()
return {'host': host, 'status': 'success', 'output': output}
except exception as e:
return {'host': host, 'status': 'failed', 'error': str(e)}
def batch_collect(devices, max_workers=10):
"""并发采集多台设备"""
results = []
with threadpoolexecutor(max_workers=max_workers) as executor:
future_map = {executor.submit(collect_device_info, dev): dev for dev in devices}
for future in as_completed(future_map):
result = future.result()
results.append(result)
return results
if __name__ == '__main__':
# 设备清单可以来自excel、csv或cmdb接口
devices = [
{'host': '192.168.56.101', 'username': 'admin', 'password': 'admin123', 'command': 'show version'},
{'host': '192.168.56.102', 'username': 'admin', 'password': 'admin123', 'command': 'show version'},
# 更多设备...
]
results = batch_collect(devices, max_workers=10)
for r in results:
if r['status'] == 'success':
print(f"设备 {r['host']} 采集成功")
else:
print(f"设备 {r['host']} 采集失败: {r['error']}")
这里 max_workers=10 表示最多同时跑10个线程,也就是同时ssh登录10台设备。线程数不是越大越好,要考虑你运维跳板机的资源、网络带宽、以及设备自身的ssh连接数限制。有些老交换机ssh并发数有限制,同时登录太多可能直接拒绝新连接。我实际使用中,20台以内设备并发数设5-10比较稳妥,上百台设备可以适当提高到20-30,但建议分段执行。
用线程池还有个好处: as_completed 是"谁先完成谁先返回",不会因为某一台设备卡住而阻塞其他设备的采集。我遇到过某台设备ssh握手特别慢,如果不加超时控制,串行执行时那一台就能拖慢整个批次。
4. 批量配置下发的正确姿势:模板化与回显确认
4.1 配置下发与查询的本质区别
批量查询是把设备状态"读"出来,相对安全;批量配置下发是往设备上"写"操作,风险级别完全不同。一个不留神把配置下发到错误的设备上,或者在设备上执行了错误的命令,轻则业务中断,重则设备失联需要跑机房。
所以配置下发脚本,和查询脚本在架构设计上必须有本质的区别。我的经验是至少要满足以下几个条件:
- 设备分组和配置模板分离 :不同设备、不同角色,下发的配置可能不一样。比如核心交换机要下发路由协议相关配置,接入交换机只需要下发vlan和端口配置。脚本应该能将"设备分组"和"配置模板"灵活匹配,而不是把所有设备一视同仁地灌同一段配置。
- 干跑模式(dry-run) :正式执行之前,先通过脚本生成每台设备的配置变更内容,人工或自动review一遍。这个和ansible的
--check模式类似。 - 自动保存配置 :配置下发后要自动执行保存命令(思科是
write memory,华为是save),避免设备重启后配置丢失。 - 结果验证 :不能只管下发不管效果。比如下发完vlan配置后,要执行
show vlan brief验证一下vlan是否创建成功;下发完ntp后,要执行show ntp status确认ntp同步状态。
4.2 多厂商设备差异化处理的实用技巧
实际网络环境里,很少存在单一厂商的情况,常见的是思科、华为、锐捷、h3c混用。不同厂商的命令体系有差异,比如思科是 show running-config ,华为是 display current-configuration ;思科进入特权模式是 enable ,华为是 system-view 进入系统视图。
paramiko脚本如果不处理厂商差异,批量执行时就会很痛苦。我的做法是在设备清单里增加一个 vendor 字段,根据厂商选择对应的命令模板:
def get_commands_by_vendor(vendor, action, params=none):
"""根据厂商和动作返回对应的命令列表"""
if vendor == 'cisco':
if action == 'backup':
return ['show running-config']
elif action == 'save':
return ['write memory']
elif action == 'config_ntp':
return [f"configure terminal", f"ntp server {params['ntp_server']}", "end", "write memory"]
elif vendor == 'huawei':
if action == 'backup':
return ['display current-configuration']
elif action == 'save':
return ['save', 'y']
elif action == 'config_ntp':
return [f"system-view", f"ntp-service unicast-server {params['ntp_server']}", "return", "save", "y"]
elif vendor == 'h3c':
# h3c命令和华为很像但不是完全一样
if action == 'backup':
return ['display current-configuration']
elif action == 'save':
return ['save force']
# 其他厂商继续扩展...
这块功能看起来简单,但坑非常多。比如ntp命令,思科支持 ntp server ,华为支持 ntp-service unicast-server ,h3c支持 ntp-service unicast-server 但也兼容 ntp server ,锐捷不同版本命令也不一样。真的到了生产环境,你会发现"同一个厂商不同版本"的命令差异也很大,只能靠设备清单里的 vendor 和 version 字段、以及按具体型号微调命令模板来解决。
一个更优雅的方案是用netmiko这个基于paramiko二次封装的库,它自带多厂商命令映射和交互式命令处理方法,但我们现在讲的是paramiko的实现思路,所以先用最朴素的方式处理厂商差异。
4.3 配置变更后的自动验证机制
配置下发完成后,一定要有自动验证机制。我见过太多人写完配置脚本后只在终端看到"命令执行成功"就以为万事大吉,结果过了几天设备出问题排查才发现某个配置压根没生效。
验证机制要在脚本里显式实现,我常用的做法是: 下发完配置后,立即执行查询命令,然后把输出里的关键字和设备是否正常关联起来 。举个例子,下发ntp配置后,查询 show ntp status ,检查输出里是否有"synchronized"或者"clock is synchronized"字样,有就标记为成功,没有就标记为待人工确认。下发syslog服务器配置后,查询 show logging 检查服务器地址是否在列表里。
def verify_config(host, username, password, vendor, action, params):
"""验证配置是否生效"""
# 执行保存
save_cmds = get_commands_by_vendor(vendor, 'save')
exec_commands(host, username, password, save_cmds)
# 验证配置
if action == 'config_ntp':
verify_cmds = get_commands_by_vendor(vendor, 'verify_ntp')
output = exec_commands(host, username, password, verify_cmds)
if vendor == 'cisco':
if 'synchronized' in output or 'synchronized' in output:
return {'host': host, 'status': 'ok', 'detail': 'ntp已同步'}
else:
return {'host': host, 'status': 'warning', 'detail': output[:200]}
验证逻辑的核心是:脚本只能帮你发现"明显不对"的情况,对于"看起来对但可能不对"的情况,要标记为warning交给人工判断,而不是强行判定成功或失败。我在实际项目中见过一个ntp配置验证案例,设备上配置的ntp服务器地址可达但设备始终没达到同步状态,脚本给出了warning,人工排查发现是设备到ntp服务器之间有防火墙拦截了123端口——如果脚本只判断"命令执行成功"就会漏掉这个故障。
5. 实测中踩过的坑:编码、分页、超时与交互
5.1 中文设备名和回显乱码:编码处理是第一个拦路虎
国内很多网络设备的设备名、描述信息、snmp团体名都是中文。ssh终端处理这些中文字符时,设备和客户端两端编码不一致就会出现乱码。paramiko默认的 decode('utf-8') 不是万能解药,思科老设备默认可能用ascii或者latin-1,华为设备vrp默认gbk编码的场景我也遇到过。
最稳妥的做法是读取回显时用 errors='ignore' 忽略无法解码的字节,避免抛出 unicodedecodeerror 异常导致脚本崩溃。然后实际内容如果出现乱码,再用 output.encode('latin-1', errors='ignore').decode('utf-8', errors='ignore') 这类方式尝试转码。
我踩过的真实案例是:一台华为s5720交换机,设备名为"三层-核心-a",display version输出里有中文,用 decode('utf-8') 直接正常,但另一台老版本s5700输出同样的中文却用gbk编码,utf-8解码直接报错。后来我写了一个通用的解码函数:
def smart_decode(data: bytes) -> str:
"""尝试多种编码解码ssh回显"""
for encoding in ('utf-8', 'gbk', 'gb2312', 'latin-1'):
try:
return data.decode(encoding)
except unicodedecodeerror:
continue
return data.decode('utf-8', errors='ignore')
虽然不优雅,但胜在实用。ssh回显这种场景,用多重编码尝试基本能覆盖常见设备。
5.2 分页输出导致命令"卡死"的经典陷阱
这是paramiko批量执行命令时最经典的坑:很多设备的show命令默认分页显示,比如思科的 show running-config 输出比较长,到一屏底部会出现"--more--"提示,等待用户按空格或回车继续显示。paramiko的 shell.recv() 只接收了第一屏数据,你以为命令已经执行完了,但设备还在等待你发送翻页指令,后续命令就没法正常执行了。
这个问题有三种解法:
第一种,执行命令前先关闭分页功能。思科设备在特权模式下执行 terminal length 0 ,华为设备执行 screen-length 0 temporary ,锐捷是 terminal length 0 。这是最干净的办法,强烈推荐在脚本开头统一执行:
def disable_pagination(shell, vendor):
"""关闭设备分页显示"""
if vendor == 'cisco':
shell.send('terminal length 0\n')
elif vendor == 'huawei':
shell.send('screen-length 0 temporary\n')
elif vendor == 'h3c':
shell.send('screen-length disable\n')
# 等待命令生效
time.sleep(1)
shell.recv(65535)
第二种,发送命令时在命令末尾追加"分页不暂停"的参数。这种方式依赖具体命令,不够通用。比如思科 show running-config | no-more 。
第三种,在接收循环里检测"--more--"字符串,检测到就发送一个空格字符继续翻页。这种方法适用于那些无法关闭分页的极端情况,但实现起来要处理很多边界条件,比如翻页命令需要多长的间隔、怎么判断输出已经完全结束等等。
实际项目里我强烈建议用第一种方法,简单有效。但有两点要特别提醒: terminal length 0 只对当前ssh会话有效,断开后自动恢复,不用害怕影响设备配置;华为的 screen-length 0 temporary 中的 temporary 参数很关键,不加的话会把配置写入设备配置文件里,相当于修改了设备配置,这在批量巡检场景下是绝对不允许的。
5.3 超时与设备无响应:监控机制和异常隔离
paramiko脚本在批量执行时,最怕的就一个设备出问题导致整个脚本卡住。我的经验是设置三层超时+一层异常隔离。
第一层是ssh连接超时,通过 connect() 的 timeout 参数设置,一般10秒足够;第二层是ssh协议协商超时,通过 banner_timeout 和 auth_timeout 设置;第三层是命令执行超时,这个paramiko没有直接提供参数,需要自己在代码里控制——实时监控接收缓冲区,如果超过n秒没有新数据,就认为命令执行超时或者设备无响应,主动断开连接。
异常隔离指的是:每一台设备都应该在独立的try-except块里执行操作,某一台设备报错只影响它自己,不影响整个批量任务的推进。我见过一个失败的脚本设计:循环里不包try-except,第5台设备连接超时抛异常,整个脚本退出,后面25台设备全部没跑。这是批量操作脚本里最low的错误,却反复有人踩。
我常用的处理模式是这样:
def safe_execute(device):
"""单台设备操作的安全执行包装器"""
try:
result = execute_on_device(device)
return {'host': device['host'], 'status': 'success', 'data': result}
except paramiko.authenticationexception:
# 认证失败单独捕获,这类错误多半是密码错了
return {'host': device['host'], 'status': 'auth_failed', 'error': '用户名或密码错误'}
except paramiko.sshexception as e:
# ssh协议层面的错误
return {'host': device['host'], 'status': 'ssh_error', 'error': str(e)}
except socket.timeout:
# tcp连接超时
return {'host': device['host'], 'status': 'timeout', 'error': '连接超时'}
except exception as e:
# 兜底异常
return {'host': device['host'], 'status': 'unknown_error', 'error': str(e)}
有了这个包装器,批量任务结束后你能清楚地看到:哪些设备成功了、哪些设备认证失败、哪些设备连接超时,然后针对失败的那几台单独排查即可。
5.4 敏感操作的安全网:回滚与确认机制
配置下发脚本多了一个"确认"机制。尤其在批量重启端口、清空配置、修改管理ip这类高风险操作上,我的原则是: 能不加自动化就不加自动化,必须加自动化的,至少保留手动确认环节 。
一种可行的做法是:脚本先生成每台设备将要执行的命令清单(模板渲染结果),写入一个文件,程序打印出"确认执行以下命令吗?"等待人工输入 yes 后才真正连接设备下发。批量巡检可以全自动,批量变更建议半自动。
另外强烈建议在脚本启动前自动对设备做一次配置文件备份,并存到本地指定目录。一旦变更导致设备异常,可以快速恢复配置。备份操作很简单,就是ssh登录后执行显示配置命令,把回显保存成文本文件。别小看这一步,紧急故障时一份昨天的配置文件备份可能帮你省掉一晚上的排障时间。
6. 从paramiko走向更完善自动化方案:一个过来人的进阶路线
6.1 在实际项目中如何组织paramiko项目代码
脚本写得多了,自然会发现把工具函数、设备清单、命令模板、主逻辑混在一个文件里,维护起来越来越痛苦。我现在的paramiko项目通常按照下面的目录结构来组织:
netauto/
├── conf/
│ └── devices.yaml # 设备清单(ip、厂商、账号信息)
├── libs/
│ ├── ssh_client.py # 封装ssh连接与命令执行
│ ├── command_mapper.py # 多厂商命令映射
│ └── backup.py # 配置文件备份模块
├── templates/
│ ├── ntp.j2 # jinja2配置模板
│ └── snmp.j2
├── scripts/
│ ├── batch_collect.py # 批量查询入口
│ └── batch_deploy.py # 批量配置下发入口
├── backup/ # 配置文件备份存放目录
└── logs/ # 执行日志目录
设备清单用yaml文件维护比硬编码在脚本里好得多。yaml文件里可以包含每台设备的主机名、管理ip、厂商、型号、所属站点、角色,以及该设备需要下发哪些配置模板所需的参数。
devices:
- hostname: core-sw-a
host: 192.168.56.101
vendor: huawei
role: core
site: beijing
params:
ntp_server: 192.168.10.10
snmp_community: "public@2024"
- hostname: acc-sw-01
host: 192.168.56.102
vendor: cisco
role: access
site: beijing
params:
ntp_server: 192.168.10.10
snmp_community: "public@2024"配置模板用jinja2渲染,根据设备的vendor和role来匹配,这样配置下发的逻辑可以做到"设备清单变、模板变、脚本不变"。
6.2 进阶方向:netmiko、nornir与运维平台的演进思路
搞清楚了paramiko的底层原理和手动实现后,你可以尝试使用更上层的自动化工具来简化工作。这里我根据自己的使用经验,说说这几个工具的定位差异:
netmiko 是基于paramiko封装的多厂商网络设备操作库。它解决的最大痛点就是多厂商命令差异和交互式命令处理。比如你想在华为设备上执行 save 命令,设备会提示 are you sure to continue? [y/n] ,netmiko可以通过 expect_string 参数自动匹配提示并发送"y"。用paramiko你得自己处理这些交互逻辑,用netmiko就是你声明"我要执行这个命令,期望看到这个提示,然后回复y"。
nornir 是纯python编写的网络自动化框架,比ansible更轻量,并且利用python的并发机制做并行执行做得很好。nornir可以管理设备清单(inventory)、连接信息,内置了多线程并行机制,你可以用很小的代码量完成"对所有核心交换机执行指定任务"这类操作。
如果公司有正式的运维平台,建议把paramiko脚本封装成api或命令行工具,集成到平台的"设备操作"或"自动化作业"模块中。这样设备操作就有审计、有审批、有记录,比每个人都拿脚本去跑要可控得多。
6.3 给新人的建议:先会手动,再谈自动化
最后说一点经验之谈。paramiko脚本写得再花哨,前提是你必须本身熟悉网络设备的操作。我见过有人脚本写得很好,但连 show ip interface brief 的输出都看不懂,不知道哪台设备接口down了,这种人写出来的自动化脚本只能算是"能跑",谈不上"好用"。
如果你刚接触网络自动化,我建议的学习路径是:
- 先熟练掌握至少一种主流厂商设备的手动配置和排查命令,知道常见的show命令输出长什么样,理解vlan、路由、ntp、snmp这些基础概念。
- 然后用paramiko写简单的单台设备巡检脚本,逐步扩展到批量查询、备份。
- 接着用paramiko实现批量配置下发,重点体会"配置命令生成、下发、确认、验证"这条链路的工程细节。
- 等对ssh协议和paramiko机制都很熟了,再去尝试netmiko、nornir等更高级的框架,甚至ansible。
我个人在带新人时发现一个很有用的练习任务:用paramiko写一个脚本实现"批量备份设备配置,并按设备名+日期命名备份文件,最后生成一份执行结果汇总表"。别小看这个需求,它覆盖了paramiko连接、命令执行、回显读取、文件操作、结果统计和异常处理这几大关键模块,一口吃下这个任务基本就掌握了paramiko的精髓。
另外,写批量执行脚本时一定要记得幂等性设计。一条配置命令执行一次和执行十次,最终结果应该是一样的,这样才能放心地在不同时间反复运行脚本。比如下发ntp配置,你应该先判断设备上是否已经配置了该ntp服务器,有则跳过,没有才添加;而不是无脑地每次都添加一遍,导致设备上出现重复的ntp服务器条目。这些细节虽然不起眼,但恰恰是生产环境中脚本能不能反复可靠运行的命门。
到此这篇关于python paramiko实现批量管理网络设备的实践教学的文章就介绍到这了,更多相关python paramiko管理网络设备内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论