简介:面向网络运维与IT自动化人员,提供一套基于Python与SSH协议的网络设备自动化管理实现方案,尤其适用于中小型网络场景下路由器与交换机的批量配置与状态巡检。包体为单个docx文档,大小1.89MB,内容完整覆盖设计思路、环境搭建与核心代码,可直接对照eNSP拓扑进行实操。方案以paramiko模块建立SSH连接,借助re与time实现命令交互与输出解析,可实现设备版本、接口状态、IP地址、内存使用等信息的自动查询,以及用户配置、访问控制和配置文件备份等自动化操作。通过华为eNSP模拟器与FreeSSHd组合验证,帮助读者降低人工重复操作,提升运维准确性。已有107人学习下载,适合具备一定Python基础、希望拓展网络自动化技能的工程师及计算机专业学生。 设备多了以后,光靠手敲命令逐台登录设备做配置、做巡检,效率低不说,还容易敲错。这几年我在网络运维岗上最大的一个体会就是:凡是重复的、有规律的、需要批量执行的操作,都值得用脚本去替代。Python配上SSH协议,就是一套门槛最低、见效最快的自动化组合。这篇文章就围绕“Python + SSH”这条路,讲清楚网络设备自动化管理怎么做、为什么这么做、实际落地时会踩哪些坑。
1. 整体设计与思路拆解
1.1 网络运维的重复痛点,为什么偏偏选SSH
先别急着写代码,想清楚一个问题:网络设备自动化有很多种路子,SNMP、NETCONF、RESTCONF、Telemetry,都有各自的适用场景,为什么日常工作中用得最多的反而是SSH?
原因不复杂。SNMP能帮你读取设备状态,但改配置的能力很弱;NETCONF/RESTCONF是好东西,可不少老旧设备、低端设备压根不支持;而SSH是几乎所有网络设备都标配的远程管理协议,交换机、路由器、防火墙、无线控制器,无论哪个厂商,只要你平时能用SecureCRT或Xshell登录上去敲命令,就说明设备一定开了SSH服务。这意味着你用Python的SSH库写一套脚本,就能通吃全场设备,不用关心设备是华为、H3C、思科还是锐捷,只要它会用命令行交互就行。
另外还有个现实因素:运维现场往往有各种安全策略、跳板机、堡垒机,很多环境下图形化网管接口不一定开放,但SSH 22端口大概率是通着的。与其纠结要不要上一套重量级自动化平台,不如先用手边的Python把SSH这条链路打通,马上就能看到效率提升。
1.2 用Paramiko还是Netmiko,这个选择很关键
确定走SSH这条路后,首先要选Python库。Paramiko是Python生态里最底层的SSH协议实现,它的定位是帮你建立SSH连接、执行命令、传输文件,说白了就是给你一把“裸工具”。而Netmiko是建立在Paramiko之上的网络设备专用封装库,它内置了几十种厂商设备类型的交互规则,能够自动处理不同设备的命令行提示符、分页符、特权模式切换等差异。
我自己的建议是:如果你只是临时跑个命令、批量备份配置,或者想深入理解SSH交互的过程,用Paramiko没问题;但如果目标是长期维护一套设备管理脚本,一定要用Netmiko。原因很简单,Netmiko帮你解决了一个特别烦人的问题——设备返回大量信息时分页暂停(比如---- More ----),用Paramiko你得自己手动发空格去翻页,而Netmiko会默认关闭分页并自动处理这些交互细节。
1.3 自动化脚本的设计思路
在实际规划这套自动化工具时,我遵循一个原则:连接层、命令层、业务层三层分离。
连接层统一负责SSH会话建立、认证、断开,不管底层是Paramiko还是Netmiko,对上提供统一的接口;命令层负责执行具体命令、判断返回结果、处理异常;业务层才是真正跟需求相关的部分,比如“每日自动备份所有交换机配置”、“批量把几十台设备的NTP地址改掉”、“自动收集设备CPU和内存使用率”。
这样做的好处非常明显,三层分离之后,更换连接库、增加新设备型号、修改业务命令,相互之间都不会影响。哪怕你今天用的是华为设备,明天要管理思科设备,只要你把设备类型这个参数改掉,脚本主体完全可以复用。
2. 核心细节解析与实操要点
2.1 设备登录认证:密码和密钥两种方式的取舍
SSH登录网络设备,通常有两种认证方式:密码认证和密钥认证。
密码认证最容易理解,账密配置好直接登录。但这里有个坑:企业网络的设备账密往往有定期轮换的合规要求,而且不同批次设备的密码可能还不一样。如果脚本里硬编码密码,密码一换脚本就崩;如果写在配置文件里,文件本身又需要做好权限管控。
密钥认证就灵活得多。网络设备支持在客户端生成一对RSA密钥,把公钥配置到设备上,之后SSH登录时,只要客户端持有私钥,就能免密登录。这样做的好处是:脚本里不需要存密码,认证凭据是密钥文件本身;而且密钥可以设置独立的访问权限,比如只允许指定账号通过该密钥登录。
我在实际项目里,面对上百台设备批量管理时,几乎全部使用密钥认证。运维人员可以在自己的电脑或者跳板机上生成密钥对,将公钥分发到每台设备的管理账号下。之后脚本运行时,只要密钥文件路径配置正确,无需任何交互输入密码就可以完成全部认证流程,这在配合Jenkins定时执行任务时优势尤其突出。
2.2 Netmiko核心参数配置
Netmiko使用起来非常简单,核心就是创建一个连接对象:
from netmiko import ConnectHandler device = { "device_type": "huawei", "host": "192.168.10.1", "username": "admin", "password": "your_password", "port": 22, "secret": "enable_password", # 如果需要进入特权模式 "timeout": 30, "conn_timeout": 15, } conn = ConnectHandler(**device)这里面的参数有讲究。device_type决定Netmiko用什么交互规则,常见值有huawei、cisco_ios、hp_comware、ruijie_os等,选项很多,用之前最好去官方文档查一下你的设备型号对应哪个值。timeout指的是命令执行的超时时间,conn_timeout是TCP连接建立的超时时间,批量操作用的比较多的时候,这两个超时建议设置得宽裕一些,避免因网络抖动导致脚本大面积失败。
再说说secret。华为设备进入系统视图不需要单独的特权模式,但思科设备从用户模式进入特权模式需要执行enable命令。Netmiko的secret参数就是干这个用的。如果你的设备只需要普通命令就能完成操作,可以不用这个参数。
2.3 对设备返回信息的判读是自动化的核心
执行命令后,设备返回的信息可能是命令回显、也可能包含错误提示。脚本要做的不仅仅是“把命令发出去”,更重要的是判断命令是否真的执行成功。
Netmiko提供了一个send_command方法,返回的是命令输出的文本。判断成败常用的手段是检查输出中是否包含特定的错误关键字,比如% Unknown command、Error: Unrecognized command、Invalid input detected at '^' marker等。我写脚本时通常会把一个命令的输出标准定义好,然后用in运算符做关键字匹配,这是最简单也最可靠的办法。
另外,不少初学者容易忽略的一点是:先进入设备的配置模式再执行配置命令。比如华为设备,如果不先执行system-view就试图配置接口,设备会直接报错。一些老牌厂商的模拟器环境对这种细节特别严格,玩过eNSP的人都懂。所以脚本里配置类命令的序列往往是:system-view、逐条下发配置、return或quit退出配置模式。
3. 实操过程与核心环节实现
3.1 环境准备与依赖安装
动手之前先把Python环境准备好。Windows、Linux、macOS都可以,Python 3.6以上版本就能顺利运行Netmiko。安装依赖只需要一条命令:
pip install netmiko paramikoNetmiko会自动把Paramiko作为依赖装上,所以直接装Netmiko就够了。如果你在公司内网环境,装不了PyPI源,可以在有外网的机器上下载好wheel包,再拷贝到内网机器本地安装。
个人建议先把基础连通性测试做扎实,再用脚本去连设备。具体做法是:先用你平时习惯的SSH客户端(Xshell、SecureCRT、MobaXterm均可)手动登录目标设备,确认账号、密码、端口、SSH协议版本都没有问题。这样脚本失败时,你可以确定问题出在脚本侧而不是基础网络侧,排查范围至少缩了一半。
3.2 批量备份交换机配置
这个脚本是我在运维日常中复用得最频繁的,核心功能是批量登录多台设备,执行display current-configuration(华为)或show running-config(思科),把输出保存为本地文件,文件名带上设备名和日期。
import os import datetime from netmiko import ConnectHandler devices = [ {"device_type": "huawei", "host": "192.168.10.1", "username": "admin", "password": "Pass@123"}, {"device_type": "huawei", "host": "192.168.10.2", "username": "admin", "password": "Pass@123"}, ] today = datetime.datetime.now().strftime("%Y%m%d") backup_dir = f"backup_{today}" os.makedirs(backup_dir, exist_ok=True) for dev in devices: host = dev["host"] print(f"开始备份 {host} ...") try: conn = ConnectHandler(**dev) conn.enable() # 如果需要进入特权模式 output = conn.send_command("display current-configuration") conn.disconnect() filename = os.path.join(backup_dir, f"{host}_{today}.txt") with open(filename, "w", encoding="utf-8") as f: f.write(output) print(f"{host} 备份成功,文件:{filename}") except Exception as e: print(f"{host} 备份失败:{e}")这段代码里有一个细节值得说明:文件保存编码用的是utf-8。华为设备返回的配置文本里包含中文备注时,如果直接按系统默认编码(Windows下是GBK)保存,后续用文本编辑器查看很容易乱码。统一用UTF-8保存,配合VS Code或Sublime Text查看就没问题。
3.3 批量修改设备配置
备份是读取操作,配置变更是写入操作,风险系数完全不同。批量修改设备配置前,我是强烈建议先把配置命令写在一个文本文件里,脚本按需读取并逐条下发,尽量减少在代码里硬编码命令的条数。这样命令的审核、变更留痕、后续维护都会方便许多。
# commands.txt system-view ntp-service refclock-master 1.1.1.1 ntp-service unicast-server 202.118.1.1 return save force脚本读取该文件后,用send_config_set方法一次性下发全部命令:
from netmiko import ConnectHandler from netmiko.exceptions import ConfigInvalidException device = { "device_type": "huawei", "host": "192.168.10.3", "username": "admin", "password": "Pass@123", } with open("commands.txt", "r", encoding="utf-8") as f: commands = [line.strip() for line in f if line.strip() and not line.startswith("#")] conn = ConnectHandler(**device) output = conn.send_config_set(commands) print(output) conn.disconnect()注意send_config_set和send_command的区别:send_config_set专门用于发送配置类命令,自动进入配置模式并退出配置模式;send_command用于发送普通查询命令。如果你混用,轻则命令不生效,重则直接把设备搞进一个奇怪的模式里,再往后执行什么都会报错。
还有一点,脚本里最后一行是save force。华为设备的配置修改后如果不保存,重启就会丢失。save force会跳过交互确认,直接保存配置。在实际操作中,变更结束后是否立即保存,需要根据变更管理流程来决定。有的场景要求先观察一段时间再保存,如果脚本默认保存了,反而可能让有问题的配置固化到设备里,这一点务必要和你的变更策略对齐。
3.4 批量巡检:收集设备关键状态
除了备份和配置,巡检是另一个高频场景。用send_command执行display device、display cpu-usage、display memory-usage,把结果汇总到一个汇总文件,就能实现一个最简版本的“巡检机器人”。
from netmiko import ConnectHandler devices = [ {"device_type": "huawei", "host": "192.168.10.1", "username": "admin", "password": "Pass@123"}, {"device_type": "huawei", "host": "192.168.10.2", "username": "admin", "password": "Pass@123"}, ] with open("inspection_report.txt", "w", encoding="utf-8") as report: for dev in devices: host = dev["host"] report.write(f"========== {host} ==========\n") try: conn = ConnectHandler(**dev) report.write("【CPU】\n" + conn.send_command("display cpu-usage") + "\n") report.write("【内存】\n" + conn.send_command("display memory-usage") + "\n") report.write("【设备信息】\n" + conn.send_command("display device") + "\n") conn.disconnect() except Exception as e: report.write(f"ERROR: {e}\n") report.write("\n")这个脚本的编写思路同样遵循“能并行就并行”的原则,因为设备多的时候,串行执行会特别耗时。但要注意:并行只是把脚本挂个线程池,Netmiko本身对单台设备的操作还是同步的。用concurrent.futures.ThreadPoolExecutor把每台设备丢到一个线程里去执行,设备数量多的时候效率提升会非常明显。
4. 常见问题与排查技巧实录
4.1 认证失败与SSH配置问题
SSH连接报认证错误,排查思路应该按“网络可达 → 端口通 → 协议匹配 → 账密正确 → 授权足够”的链路逐层进行。
先说网络和端口,用ping测网络连通性,用telnet ip 22测试端口是否可达。然后是SSH协议版本,Python的Paramiko默认支持SSH v2,如果你的设备只开了SSH v1,认账就是谈不拢。账密问题则要检查设备账号是否被锁定、密码是否过期、是否配置了ACL限制管理地址。最后别忽略授权问题,很多设备账号是低权限的,只能看不能改,执行配置命令就会报权限不足。
密钥认证如果连不上,优先检查密钥权限。在Linux跳板机上,私钥文件的权限必须是600或400,权限过宽SSH直接拒绝使用该密钥。Windows上用密钥时,用ssh-keygen生成的私钥格式有些老设备不认,需要转换成OpenSSH格式。
4.2 命令输出超时或被截断
批量执行命令时,如果命令本身比较耗时(比如display current-configuration在配置量大的设备上可能要好几秒),默认超时时间可能不够。Netmiko的send_command可以显式设置超时时间:
output = conn.send_command("display current-configuration", read_timeout=60)如果命令返回的内容特别长,还有一个隐藏问题:终端宽度。设备默认的终端宽度可能是80列,超宽部分会被折行或者截断,导致输出的配置内容不完整。Netmiko在连接后会自动把终端宽度调整为较大的值(通常是511列),但如果你用Paramiko裸连,就得记得手动设置。
4.3 设备返回编码乱码
SSH协议传输的是字节流,Netmiko会按设备的编码规则解码。绝大多数设备默认是UTF-8,但有些华为老设备会用GBK编码输出中文。如果你发现输出里中文全是乱码,可以在Netmiko连接参数里加一个encoding参数试一下:
device = { "device_type": "huawei", "host": "192.168.10.1", "username": "admin", "password": "Pass@123", "encoding": "gbk", }这个问题在手工操作时根本感知不到,人眼能看懂乱码背后的意思,脚本就完全不行。批量巡检时如果脚本里有关键字匹配逻辑,一旦编码不对,匹配必然失败,甚至会把正常信息识别成异常信息。
4.4 多厂商设备指令差异
不同品牌设备命令差异很大,这是网络自动化绕不开的痛。思科的show running-config对应华为的display current-configuration,思科的接口模式对应华为的系统视图,命令行的提示符也完全不同。Netmiko用device_type来屏蔽这些差异,但它只能解决交互层的差异,命令本身还是要你自己区分。
我的做法是写一个简单的适配层,把常用操作封装成统一的接口。例如:
def get_running_config(conn): if conn.device_type.startswith("huawei"): return conn.send_command("display current-configuration") elif conn.device_type.startswith("cisco"): return conn.send_command("show running-config") else: raise ValueError(f"暂不支持该设备类型:{conn.device_type}")额外提醒一点:华为设备的VRP版本和VRP8版本之间,命令也有一定差异,比如BGP、接口下的配置子命令,有些地方不兼容。在写脚本前,最好确认好设备的具体版型,别想当然。
5. 安全加固与方案扩展方向
5.1 千万不要把密码硬编码进脚本
开发机上的脚本拿来就能跑,但这样的脚本一旦流传出去,就等同于把全网设备的管理员密码公布出来了。代码仓库可能被员工误传、离职人员可能拷走脚本、测试服务器提升权限的机器可能被攻破,任何一个环节出问题,你的设备密码都会泄露。
我习惯的替代方案是使用环境变量或者独立的配置文件。配置文件单独放,并且设置严格的读写权限,Linux下用chmod 600,Windows下只给当前用户读写权限。脚本启动时从配置文件读取账密:
import os PASSWORD = os.environ.get("DEVICE_PASSWORD", "")如果公司有密码管理系统,能通过API动态取密码,那就更好了,脚本执行时按需获取,不落地存储,这是最稳妥的办法。
5.2 从脚本到平台化:简单调度、记账、告警
单台跑脚本只是第一步,真正能发挥自动化价值的是让它定时运行、结果可追溯。用Windows自带的计划任务或者Linux的crontab,可以每天凌晨自动执行配置备份脚本,上班后打开备份目录检查文件大小和生成时间即可。
更进一步,可以把备份文件打上日期和时间戳,通过Git提交到本地仓库。这样每天的配置变更都有历史记录,设备配置被意外改坏时,可以清楚地知道是哪一天、哪台设备、改了什么。这个思路看起来简单,实际做下来会发现它的价值比预期大得多。
如果公司已经有Jenkins,也可以把脚本作为构建任务集成进去。把设备IP列表、命令文件当作参数,配置一个定时触发器,就能实现可视化的自动运维任务编排。网上搜“jenkins自动化部署”的热度一直很高,很多人拿它做应用发布,但做网络设备定时巡检同样是非常典型且实用的方向。
5.3 与Ansible等主流自动化框架的配合
如果你想走得更远,可以了解一下Ansible。Ansible的网络模块底层也是走SSH,但它的理念是用声明式的Playbook代替命令式脚本,设备配置的最终状态用YAML描述,Ansible负责计算差异、自动下发、返回结果。
| 对比维度 | Python + SSH脚本 | Ansible网络自动化 |
|---|---|---|
| 上手门槛 | 需要Python基础,写逻辑灵活 | 需要了解YAML和Ansible语法,学习曲线陡峭 |
| 设备适配 | 自己处理命令和交互细节 | 网络模块对主流厂商适配好,但部分新模块验证不足 |
| 灵活性 | 高度灵活,适合复杂自定义逻辑 | 适合标准化的配置管理,复杂逻辑需要用自定义模块补 |
| 适合场景 | 小型环境、快速开发、定制化操作 | 大规模标准化设备管理和配置合规 |
我的经验是,先用Python脚本把自动化跑起来,理解和掌握SSH交互的本质之后,再决定要不要上Ansible。两个方案不是水火不容,很多团队就是Python脚本处理临时性任务、Ansible负责常态化的配置部署,配合得非常好。
写在最后的心得
网络运维的自动化,最开始可能只是为了省事,但真正跑起来之后你会发现,它最大的价值不是“省掉手工敲命令的时间”,而是把操作过程变成了可复现、可记录、可审计的资产。以前最怕的设备配置漂移、人为误操作、变更无记录这些问题,在自动化面前都会被极大地收敛。
如果你是从零开始,我的建议是:先不要贪多求全,找一个对你最有价值的小场景下手。比如就从“每周自动备份一次核心设备配置”开始,把这一个场景彻底做落地,跑顺了之后,再往批量配置、巡检报告、告警通知的方向拓展。网络自动化没有想象的那么玄乎,它就是一步步把这些重复劳动交给脚本,让你有更多时间去处理真正需要人的判断力和经验的复杂问题。
本文还有配套的精品资源,点击获取