ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

手写Python脚本实现交换机配置自动备份与批量管理

手写Python脚本实现交换机配置自动备份与批量管理 干网络这一行的谁没经历过那种时刻——核心设备出故障紧急重启之后配置找不回来了翻遍U盘、邮箱、聊天记录发现最近一份完整的配置备份还是几个月前割接时留下的。那一刻心里是真的没底。交换机配置备份这件事听起来太基础了但恰恰是最容易被拖延、最容易出问题的日常操作。设备几十台上百台品牌还不一样今天手动登录几台明天有别的活一打岔备份计划就断了。我自己就是这么过来的。早年一台台登录设备执行命令、复制粘贴保存后来学会用Python写自动备份脚本一次跑完能顶过去一整天的重复劳动而且每天定时执行再也不用靠脑子记上周备份到哪台了。这个过程其实不难哪怕你完全没写过代码只要懂基础的网络命令照这篇文章的思路走两三个小时就能写出一个能直接用的交换机配置备份脚本。这篇文章适合网工、运维工程师也适合想找个实战练手项目的Python新手。我会从环境准备讲到脚本核心逻辑、批量处理、异常容错再到定时执行和二次开发代码都是可以直接复制运行的。1. 为什么网工值得亲手写一套备份脚本1.1 手工备份的日常困境远不止费时间很多人觉得备份配置不就是SSH登上去敲一条命令吗是单台设备确实如此但放到真实网络环境里问题就浮现出来了。首先是设备数量一个中规模的机房动辄几十台上百台设备核心交换机、汇聚交换机、接入交换机、防火墙、路由器一台台登录、执行、粘贴、保存一上午就没了。更麻烦的是品牌和系统不统一思科用show running-config华为用display current-configuration华三和H3C设备又不一样命令记混了轻则备份不到内容重则误操作。其次是遗漏和遗忘。今天忙着处理故障明天有割接等想起来备份的时候可能已经过去两三周。设备配置变更大多没有记录出问题想回退都不知道该回退到哪个版本。还有一个容易被忽略的风险频繁手动登录生产设备不小心在错误的窗口里敲了命令后果可能是灾难性的。脚本的好处在于登录哪个IP、执行什么命令是预先写死的逻辑不会点错窗口。1.2 先算清楚成本和收益再决定要不要学可能有人说商业网管软件也能做配置备份何必自己写脚本。确实大厂的网管平台功能全但价格也相当可观一套授权加上服务器、数据库、专人维护成本是实打实的。而且很多网管平台的备份功能需要额外模块实施周期长小公司根本用不起。相比之下Python脚本的硬件成本是一台能SSH到设备的电脑软件成本为零唯一的投入是学习时间。我按自己的经验算过一笔账学Python基础语法、Netmiko库的常用方法大概需要三到五天写一个能跑的备份脚本第一个版本两小时左右。而手工备份一百台设备每次至少耗费半天一个月两次就是整整一天。脚本写完后每天自动跑这个时间就完全省下来了。更不用说你掌握的是写脚本的底层能力以后做配置变更、日志采集、自动化巡检都能复用这套思路这才是真正的长期收益。2. 环境准备先把Python和Netmiko伺候好2.1 Python安装中的三个高频坑第一个坑是安装时没有勾选Add Python to PATH。很多从官网下载安装包的朋友一路默认点完打开命令行输入python提示不是内部或外部命令。解决方法是重装时勾选这个选项或者手动把Python安装目录加入系统环境变量。安装完建议打开命令行验证python --version能正常输出版本号比如Python 3.11.x就说明环境OK了。第二个坑是pip命令找不到。这和PATH问题类似但更常见。稳妥的做法是使用python -m pip来安装库而不是直接敲pippython -m pip install netmiko这条命令会明确使用当前Python环境自带的pip模块基本不会出现找不到命令的问题。如果下载速度慢可以临时换用国内镜像源python -m pip install netmiko -i https://pypi.tuna.tsinghua.edu.cn/simple第三个坑是Windows PowerShell默认禁止运行脚本报错信息通常是因为在此系统上禁止运行脚本。虽然这主要影响.ps1脚本但很多人会在PowerShell里测试Python脚本最好提前设置好当前用户的执行策略Set-ExecutionPolicy -Scope CurrentUser RemoteSigned设置为RemoteSigned表示本地创建的脚本可以运行远程下载的脚本需要签名相对安全。2.2 Netmiko和Paramiko怎么选不少教程会从Paramiko讲起它是Python里非常底层的SSH协议库功能强大但也意味着你需要自己处理很多细节建立SSH连接、激活交互式会话、发送命令、等待回显、判断命令是否执行完毕、处理字节编码、处理分页……这一套下来没写过代码的人很容易被劝退。Netmiko是在Paramiko之上封装的网络设备专用库。它内置了大量厂商设备的交互逻辑你只需要告诉它设备类型、IP、用户名、密码它就能自动完成登录、关闭分页、等待提示符、执行命令这些工作。对网络工程师来说绝大多数场景用Netmiko就够了。对比维度ParamikoNetmiko抽象层级底层SSH协议网络设备会话封装设备类型适配需要自己实现内置几十种厂商系统命令交互处理手工处理回显和超时自动识别提示符学习成本较高低适合网工入门适用场景定制化传输、文件操作配置备份、命令采集Netmiko支持思科IOS、华为VRP、华三Comware、锐捷、飞塔等多种设备类型常见的网络设备基本全覆盖。安装完Netmiko后会自动带上Paramiko依赖不需要额外安装。2.3 SSH命令交互的本质一问一答不理解SSH交互机制的人写脚本时最容易出奇怪的问题。其实原理特别简单你通过网络SSH登录设备后会看到一个命令行提示符比如思科的Router#华为的Huawei。你在提示符后面输命令按回车设备执行完会重新打印提示符意思就是命令执行完了你可以输入下一条了。整个过程就像两个人对话你说一句对方回答一句对方没说完你不能急着说下一句。在脚本里Netmiko的send_command方法做的工作就是发送命令、等待设备输出、再等待提示符出现。一旦提示符出现就说明输出结束可以返回完整结果了。理解了这一点后面遇到命令卡住、输出不全的问题就懂得往是否关闭了分页、是否等到了提示符这些方向排查。3. 核心代码手写全流程单台设备先跑通3.1 第一版脚本能连上、能抓配置、能存文件不管最后要管理多少台设备第一步永远是先把单台设备跑通。我习惯先写一个最小脚本确认能登录、能拿到配置、能保存文件再往里面加批量逻辑。下面是针对华为设备的基础版本from netmiko import ConnectHandler from datetime import datetime device { device_type: huawei, host: 192.168.1.10, username: admin, password: Admin123, port: 22, } conn ConnectHandler(**device) # 获取设备当前配置 config conn.send_command(display current-configuration) # 生成带时间戳的文件名 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) filename fbackup_{timestamp}.cfg with open(filename, w, encodingutf-8) as f: f.write(config) print(f配置已保存到 {filename}) conn.disconnect()如果是思科设备把device_type改成cisco_ios命令改成show running-config其他逻辑不变。华三设备用hp_comware和display current-configuration。这个版本跑通之后再考虑更复杂的需求。3.2 理解收发时序很多奇怪问题就明白了有朋友问我为什么有时候脚本取回来的配置只有一小段这通常和分页有关。设备默认每屏显示一定行数后会暂停等待你按空格或回车继续。Netmiko一般会自动处理这种情况但部分老设备、部分系统版本可能处理得不彻底。稳妥的做法是在send_command时带上关闭分页的参数或者提前发送关闭分页的命令# 华为设备关闭分页 conn.send_command(screen-length 0 temporary) # 思科设备关闭分页 conn.send_command(terminal length 0) # 华三设备关闭分页 conn.send_command(screen-length disable)另外send_command里有个global_delay_factor参数它控制每条命令执行后的等待倍率。如果设备响应慢、网络延迟高备份出来的内容可能不完整可以适当调大这个值config conn.send_command(display current-configuration, global_delay_factor2)这个参数的作用是让脚本在发送命令后等待更长时间给设备足够的响应时间。遇到巡检脚本经常超时的场景先调这个参数基本上能解决一大半问题。3.3 文件名规范主机名加时间戳的好处第一版脚本用backup_时间戳.cfg当文件名但这在批量场景下有个问题不同设备生成的备份文件名区分度太低。跑完一百台设备backup_20250601_103000.cfg到底对应哪台设备还得打开文件去看主机名非常低效。我的建议是提前在脚本里解析出设备的主机名拼进文件名。在Netmiko中连接设备后可以用find_prompt()获取当前提示符比如华为设备的提示符是Core-SW01思科设备是DeviceA#。简单处理一下就能得到主机名prompt conn.find_prompt() # 去掉 方括号 # 等符号 hostname prompt.replace(, ).replace(, ).replace([, ).replace(], ).replace(#, ).strip()有了主机名文件名就可以设计成这种格式Core-SW01_192.168.1.10_20250601_103000.cfg主机名在最前面方便按设备查找IP放中间避免两台设备重名时间戳放最后方便按时间排序一眼看出哪个是最新备份。按日期排序时这种YYYYMMDD_HHMMSS格式的字符串天然就是有序的这是故意设计的。from datetime import datetime timestamp datetime.now().strftime(%Y%m%d_%H%M%S) filename f{hostname}_{device[host]}_{timestamp}.cfg4. 从一台到一百台批量备份与容错处理4.1 用CSV设备清单驱动批量备份单台跑通之后下一步就是把设备清单外部化。不要直接把设备信息写死在脚本里那样每加一台设备就要改代码太不优雅。最简单的方式是维护一个CSV文件用表格软件或者记事本都能编辑device_type,host,username,password,port huawei,192.168.1.10,admin,Admin123,22 cisco_ios,192.168.1.20,admin,Cisco456,22 hp_comware,192.168.1.30,admin,H3C789,22 huawei,192.168.2.10,netadmin,Admin123,22然后修改脚本用标准库的csv模块读取清单循环对每台设备执行备份import csv import time from datetime import datetime from netmiko import ConnectHandler def backup_device(device): try: conn ConnectHandler(**device) prompt conn.find_prompt() hostname prompt.replace(, ).replace(, ).replace([, ).replace(], ).replace(#, ).strip() timestamp datetime.now().strftime(%Y%m%d_%H%M%S) filename fbackup/{hostname}_{device[host]}_{timestamp}.cfg if device[device_type] in [cisco_ios, cisco_nxos]: config conn.send_command(show running-config) else: config conn.send_command(display current-configuration) # 备份前先确保目录存在 import os os.makedirs(backup, exist_okTrue) with open(filename, w, encodingutf-8) as f: f.write(config) print(f[OK] {device[host]} - {filename}) conn.disconnect() return True except Exception as e: print(f[ERROR] {device[host]} 备份失败: {e}) return False def main(): devices [] with open(devices.csv, r, encodingutf-8) as f: reader csv.DictReader(f) for row in reader: devices.append({ device_type: row[device_type], host: row[host], username: row[username], password: row[password], port: int(row[port]) if row[port] else 22, }) success 0 for device in devices: if backup_device(device): success 1 time.sleep(1) print(f本次备份完成共 {len(devices)} 台成功 {success} 台失败 {len(devices) - success} 台) if __name__ __main__: main()这里有两个实用细节。一是用os.makedirs(backup, exist_okTrue)确保备份目录存在避免因为第一次运行没有目录而报错。二是每台设备备份完time.sleep(1)让设备有足够时间释放会话资源避免连续SSH登录触发设备侧SSH并发限制。4.2 异常捕获与失败重试策略脚本运行在生产环境最怕的不是设备全部失败而是部分失败但你不知道。比如某台设备密码被改过、某台设备网络不通、某台设备SSH服务异常如果不做异常处理程序可能在第一台就崩溃退出后面的设备全部没备份或者失败信息淹没在输出里你根本注意不到。Netmiko提供了一些特定异常类型建议针对性地捕获异常类型触发场景处理建议NetmikoTimeoutException设备不可达、SSH端口不通、设备侧超时检查网络连通性和管理地址NetmikoAuthenticationException用户名或密码错误核对凭据重点检查是否被修改过socket.error/ConnectionRefusedError端口被拒SSH服务未开启确认设备是否启用了SSH服务其他Exception各类意外情况记录日志继续处理下一台在main函数里我已经用try...except Exception把单台设备的异常兜住了这样即使某一台失败也不会影响整个批次的执行。真正跑批量时我建议再加一个简单的统计输出打印成功和失败数量这样一眼就能看出结果。另外提醒一个容易踩的坑失败重试要慎重。有些朋友看到超时错误就立即重试结果设备SSH会话还没释放连续重试会触发设备的防暴力破解机制反而把管理IP锁了。我的习惯是只在网络不通这类明确超时场景下重试一次并且中间至少间隔10秒认证失败类的错误不重试——密码错了重试多少次都没用还容易锁账号。4.3 一个经常被忽略的校验备份内容不能为空脚本逻辑跑通后还有一个非常重要的检查备份出来的文件内容是不是真的有效。有时候命令本身有分页残留、设备回显了错误提示、或者登录后卡在某个菜单界面send_command拿回来的可能是空字符串、错误信息或半截内容。如果不去校验脚本会兴高采烈地把一个空文件命名为备份成功这个文件在关键时刻会害死人。我的做法是在保存前做一次简单校验if config.strip(): with open(filename, w, encodingutf-8) as f: f.write(config) print(f[OK] {device[host]} 配置已保存大小 {len(config)} 字节) else: print(f[WARN] {device[host]} 备份内容为空本机未保存)更进一步可以检查配置里是否包含该设备的关键字。比如思科配置里肯定有hostname华为配置里肯定有sysname如果连这些关键字都没有说明拿到的回显很可能是登录横幅、菜单页或者错误提示if sysname in config or hostname in config: # 内容有效正常保存 else: # 内容异常单独记录这一步不复杂但能拦住绝大多数假成功的情况。生产环境里数据完整性的价值永远比脚本跑完了更重要。5. 让备份真正省心的三个进阶方向5.1 定时执行Windows和Linux的差异化配置脚本写好只是第一步让它每天自动跑起来才算真正解决痛点。Windows下最简单的方式是任务计划程序创建一个基本任务触发器选每天操作选启动程序程序填python.exe的完整路径参数填脚本路径。注意别直接填python因为计划任务里可能不加载你的PATH环境变量最好把Python的完整路径写出来比如C:\Python311\python.exe。Linux或网络运维跳板机上的方式更直接用crontab# 每天凌晨2点执行备份脚本日志写入文件 0 2 * * * cd /opt/config-backup /usr/bin/python3 backup.py backup.log 21 backup.log 21的用法值得记一下它把脚本的标准输出和错误信息都追加到日志文件方便每天早上花半分钟扫一眼昨天的备份结果。如果哪天某台设备失败了日志里会明确记录是哪一台、什么原因。5.2 纳入Git做配置变更审计备份文件越攒越多其实还有更大的价值——配置变更审计。把备份目录初始化为一个Git仓库每次备份完自动提交就能通过git diff看到两台设备之间配置的差异或者同一设备不同时间的变更记录。这个思路非常实用尤其在网络割接、故障排查时能快速回答这台交换机上周到底改了什么。具体做法是在备份脚本最后加一段逻辑cd /opt/config-backup git add . git commit -m Daily config backup $(date %Y-%m-%d)或者在Python脚本里用subprocess调用系统Git命令。提醒一个细节初次使用Git时先设置好用户信息否则commit会失败git config --global user.name network-admin git config --global user.email networkexample.com配置变更审计的好处在于它能倒逼团队规范变更流程。以前没有记录谁改了什么全靠口头记忆现在每天的配置快照都在Git里改之前对比一下改之后留个commit出了问题回退、追责都有据可查。5.3 凭据安全与审计日志最后说个严肃问题脚本里的明文密码。很多人的设备清单CSV直接放着明文的SSH密码然后把这个CSV文件提交到了Git仓库或者公司共享盘上。这相当于把钥匙和锁放在同一个抽屉里一旦文件泄露所有设备都快被接管了。我个人的建议是至少做到两点第一脚本和清单文件的权限严格控制只允许相关运维账号读取chmod 600 devices.csv backup.pyWindows下则设置ACL权限禁止普通用户读取。第二优先考虑用getpass交互式输入密码或者把密码放入环境变量从环境变量中读取避免硬编码在文件里import os device[password] os.environ.get(NETWORK_DEVICE_PASSWORD)当然交互式输入密码没法完全自动化所以现实中的折中方案是在专用跳板机上存放凭据文件通过系统权限和网络安全策略限制访问范围。无论选哪种方式都别把生产设备的密码跟着脚本一起到处传。另外我一直保留一个习惯备份完成后每周随机人工抽查两三台设备的备份文件打开确认内容和设备当前配置一致。自动化不是终点自动化加人工抽验才是真正可靠的生产实践。脚本写得再好最终目的是让你在设备真的出问题的时候手里有一份随时能用、内容可信的配置档案。
返回列表