ARTICLE DETAIL

资讯详情

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

Python网络设备自动配置:从Paramiko到Netmiko实战经验

Python网络设备自动配置:从Paramiko到Netmiko实战经验 入行网络运维的头三年我干得最多的活儿就是人肉配置。几十台交换机要统一加一段ACL、改个SNMP团体名、或者批量刷新NTP时钟源那就得一台一台SSH登上去敲命令。敲到后半程脑子稍微一乱很容易在某一台上漏一条命令回头巡检才发现又得重新对比配置。后来我开始用Python做网络设备自动配置把这些重复性的操作交给脚本才算是从这种低效循环里解脱出来。这篇内容想聊的就是我从Paramiko一路用到Netmiko、再结合备份和变更校验的完整实践经验给同样每天和设备打交道的你做个参考。1. 动手之前先想清楚Python自动化到底解决什么问题不解决什么1.1 收益最大、见效最快的几种场景先说结论自动配置适合标准化、批量、可重复的操作。我日常收益最高的场景有三类。第一类是批量配置变更。比如全网接入交换机需要统一开启某个端口安全功能、统一调整SNMP参数、批量下发新的VLAN。手工做一遍要一两个小时中间还可能出错脚本跑一遍只要几分钟而且每台设备执行了什么命令、成功还是失败都会有记录。第二类是配置备份。这个很多团队都在做但是用脚本实现的效率确实高。每天早上自动把所有设备running-config抓下来存到服务器一旦配置被改坏了或者设备故障需要还原直接翻备份就行。没有备份的变更操作等于是在裸奔。第三类是合规巡检。定期检查设备上是否有不该存在的配置比如多余的远程管理地址、弱密码残留、违规的互联接口描述。用脚本批量抓取后统一过滤远比你一台一台登录去翻靠谱。1.2 哪些场景不建议硬上自动化有句话我说在前面不是所有配置操作都适合写脚本。以下三种情况我建议你谨慎。一是设备类型高度混杂的环境。如果你的网络里同时有思科、华为、H3C、锐捷还有一堆杂牌交换机而且每家的命令行风格差异很大脚本要维护的兼容分支会越来越多最后可能比手工操作还费劲。这种情况建议先统一设备品牌或先选一个主流型号试点。二是救火式的紧急变更。设备正在出故障、业务中断这时候最重要的是快速恢复你的脚本还没调试完成业务早就挂了。紧急变更优先手工处理自动化留着处理常规变更。三是交互式引导过程很复杂的操作。某些设备命令执行后会弹出一连串确认比如版本升级、License激活脚本处理交互需要额外写不少逻辑。这类操作我建议用半自动的方式脚本只负责把文件传上去、登进去关键动作由人确认后再继续。想明白边界比急着写代码重要得多。2. 选型实操Paramiko、Netmiko、NAPALM分别适合什么场景2.1 Paramiko理解了它才算理解了设备自动化的底层最早我做设备自动化用的是Paramiko。这是一个纯Python实现的SSH协议库你用它可以在脚本里直接发起SSH连接然后执行命令、读取输出。它的工作逻辑很像你在终端里的手动操作连接建立后你发一条命令然后读取设备返回的内容。下面是最基础的一段连接示例import paramiko ssh paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect( hostname192.168.1.1, port22, usernameadmin, passwordpassword, timeout10, ) stdin, stdout, stderr ssh.exec_command(display version) print(stdout.read().decode()) ssh.close()set_missing_host_key_policy这行是自动接受设备的主机指纹方便批量连接exec_command执行单条命令read().decode()拿到的就是设备返回的文本输出。Paramiko很灵活但它把很多麻烦留给了你设备输出分页要自己处理命令回显里的\r\n要自己清理设备提示符变化要自己判断更不用说不同厂商命令的差异。实际写批量脚本的时候你会发现一半以上的代码都在做这些脏活累活。2.2 Netmiko把设备差异和交互细节封装起来的实用主义Netmiko是在Paramiko之上做了一层很务实的封装专门面向网络设备。它封装了什么最关键的是两点一是针对不同厂商设备的连接参数和交互模式做了适配你只需要指定device_type二是它帮你处理了分页、提示符检测、命令回显清理这些脏活。同样的事情用Netmiko是这样写的from netmiko import ConnectHandler device { device_type: huawei, # 华为设备还有 cisco_ios、arista_eos、h3c 等 host: 192.168.1.1, username: admin, password: password, port: 22, } conn ConnectHandler(**device) output conn.send_command(display version) print(output) conn.disconnect()代码量比Paramiko少了一大半而且send_command返回的就是经过清理的设备输出没有多余的\r\n和分页符。目前我维护的脚本基本都是基于Netmiko它支持的设备类型涵盖了市面上主流厂商。对于日常的批量配置、抓取配置、执行巡检命令Netmiko的封装程度刚刚好比Paramiko省心又没有NAPALM那么重的框架约束。2.3 比Netmiko更重的选择NAPALM和Ansible什么时候用如果你的环境里设备数量几百台往上而且需要做配置回滚、对接CMDB、标准化配置模板那可以考虑NAPALM。它是一个跨厂商的网络设备自动化库最大的特点是抽象出了一套统一API比如get_facts()获取设备信息、commit_config()提交配置、rollback()回滚配置。当然它的配置推送方式是替换式的对设备原有配置的兼容性要求比较高不是所有场景都适合。Ansible则是从配置管理工具切入的用YAML写playbook适合把设备配置这件事变成基础设施即代码。但Ansible的学习曲线和运维成本更高对小团队来说可能有点重。我的建议很简单设备量不大、主要做批量命令操作选Netmiko就够了需要统一API做配置回滚再看NAPALM团队已经有一套配置管理文化再考虑Ansible。工具不是越重越好够用就行。3. 第一个可直接跑的脚本连接设备并抓取版本信息3.1 环境准备以及一个容易被忽略的前提首先是环境。这里说的环境不是指在路由器上装东西而是指你的控制机也就是跑脚本的这台电脑或服务器。我推荐用Python 3.8以上版本然后执行pip install netmikoNetmiko会同时安装paramiko、textfsm等依赖所以装这一个就够了。有一个前提容易被忽略控制机到网络设备的管理IP之间必须网络可达并且设备开启了SSH服务。很多设备默认只开Telnet或者SSH只允许特定源地址。先手工确认能SSH登录再写脚本否则你会浪费大量时间在排障上。3.2 抓版本信息并保存输出连接代码就是上面那段。但实际用的时候我们会加一个习惯动作把抓回来的信息落盘保存而不是只print到屏幕。这样既留痕也方便后面做对比。from netmiko import ConnectHandler from datetime import datetime device { device_type: huawei, host: 192.168.1.1, username: admin, password: password, timeout: 30, } conn ConnectHandler(**device) version conn.send_command(display version) print(version) ts datetime.now().strftime(%Y%m%d_%H%M%S) with open(fversion_{ts}.txt, w, encodingutf-8) as f: f.write(version) conn.disconnect()这里我用了timeout: 30单位是秒。设备响应慢的时候默认值很容易触发超时报错提前调大能省去很多莫名其妙的断连问题。3.3 连接参数和认证信息怎么处理才不至于被人吐槽新手最容易犯的错误是把用户名密码直接硬编码在脚本里。脚本一旦被别人看到或者传到仓库里等于把设备密码直接公开了。我现在的做法是交互式输入脚本运行时用getpass.getpass()提示输入密码不留在代码里。环境变量从os.environ读取适合定时任务密码存在运行用户的环境变量或密钥管理服务里。配置文件用YAML存设备清单和连接信息配置文件单独管理权限不进代码仓库。不要嫌麻烦。设备密码的泄露在安全审计里是很严重的问题养成好习惯比什么都强。4. 批量配置的正确姿势设备清单、异常隔离与结果回收4.1 设备清单怎么设计Excel、CSV还是YAML批量操作的第一步是要有一个设备清单。我见过有人把设备信息直接写在Python代码里的几十台设备堆在一起维护起来很痛苦。我更推荐用CSV或YAML管理设备清单脚本只负责读取和执行。下面是一个CSV示例device_type,host,username,password huawei,192.168.1.1,admin,password huawei,192.168.1.2,admin,password cisco_ios,192.168.1.3,cisco,password如果设备数量多、有分组需求比如按机房、按业务可以用YAML结构更清晰。但要注意密码如果写在明文配置文件里文件的系统权限一定要收紧。4.2 批量执行与异常隔离一台失败不能拖垮全部批量配置脚本的核心要求是某台设备失败不能影响其他设备继续执行执行完要输出每台设备的结果方便事后跟进。我常用的脚本骨架长这样import csv from netmiko import ConnectHandler results [] with open(devices.csv, encodingutf-8) as f: devices list(csv.DictReader(f)) for dev in devices: device { device_type: dev[device_type], host: dev[host], username: dev[username], password: dev[password], timeout: 30, } commands [ vlan 100, name office_vlan, ] try: conn ConnectHandler(**device) conn.send_config_set(commands) output conn.send_command(display vlan summary) conn.disconnect() results.append((dev[host], OK, output)) except Exception as e: results.append((dev[host], FAILED, str(e))) for host, status, detail in results: print(f{host}: {status} - {detail})send_config_set接收一个命令列表会自动进入全局配置模式逐条下发然后退出配置模式。这种方法对思科、华为、H3C都是适用的差异被Netmiko封装掉了。异常隔离的关键点在于try/except包住了每一台设备的连接和配置操作这样循环里某一台超时或者密码错误只会被记录为FAILED循环继续执行后面的设备。4.3 保存配置这个动作比想象中更容易卡脚本配置下发了但还没保存设备一重启配置就丢了。不同设备保存命令不同思科是write memory华为是save而且保存时大概率会弹确认。Netmiko里的save_config()方法官方文档说得很美好但不同设备的处理逻辑并不完全一致。以华为设备为例我实际测试中它会在交互提示上出问题最稳妥的方式是用定时交互发送output conn.send_command_timing(save, delay_factor2) if y/n in output.lower() or confirm in output.lower(): conn.send_command_timing(y, delay_factor1)send_command_timing按照设定间隔把命令发出去然后根据返回内容决定是否补发确认。这里的delay_factor是延时系数设备慢的时候调大等设备的响应更久一些再继续。配置保存这个动作建议单独写一个函数针对不同设备类型做分支处理。别想着一条save_config()走天下实测下来在各厂商设备上的表现差别不小。5. 配置备份与变更追溯脚本带来的第二份红利5.1 定时抓取全量配置让每一台设备都有后悔药批量配置脚本跑完之后紧接着应该做的动作就是备份。我自己的习惯是任何变更操作执行前先跑一遍全量备份。这样一旦改坏了至少能知道原来的配置长什么样。备份脚本的核心就三件事连接设备、执行显示配置的命令、把输出写到文件。import os import datetime from netmiko import ConnectHandler backup_dir backup os.makedirs(backup_dir, exist_okTrue) device { device_type: huawei, host: 192.168.1.1, username: admin, password: password, } conn ConnectHandler(**device) config conn.send_command(display current-configuration) conn.disconnect() hostname core_sw_01 ts datetime.datetime.now().strftime(%Y%m%d_%H%M%S) filename os.path.join(backup_dir, f{hostname}_{ts}.cfg) with open(filename, w, encodingutf-8) as f: f.write(config) print(f配置已保存到 {filename})这里有个细节文件名带上时间戳可以保留多个历史版本。我一般保留最近30天的备份再定期清理避免磁盘被占满。5.2 用difflib对比变更前后的差异快速定位改了什么备份最大的价值在于对比。配置变更之后如果怀疑脚本改到了不该改的地方用difflib就能快速看出差别。import difflib with open(before.cfg, encodingutf-8) as f1, open(after.cfg, encodingutf-8) as f2: before_lines f1.readlines() after_lines f2.readlines() diff difflib.unified_diff( before_lines, after_lines, fromfile变更前, tofile变更后, n1, ) print(.join(diff))输出格式是标准的unified diff带的行是新增配置带-的行是删除或修改前的配置。我每天会跑一次全量备份然后自动对比前一天的文件把差异汇总成一份报告。这样即使团队里有人手工改了设备我也能第一时间发现。5.3 把备份变成可追溯的变更记录备份不仅仅是存文件更是变更追溯的证据。我的做法是给备份文件再加一个元信息文件记录备份时间、操作人、关联的变更单号。这样哪天出了问题需要追责或复盘可以快速找到那台设备、那个时间点、因为哪次变更变成了现在这个样子。如果团队有Elasticsearch或者简单的日志系统可以把备份文件的哈希值、大小、抓取时间推上去做一个更轻量的审计链路。没有这套体系的话用CSV记录就够用。6. 实测最容易翻车的5类问题与对应处理6.1 分页符把输出截断了backup永远不完整第一次写备份脚本的时候我抓回来的配置总少了末尾的一大段。排查了半天发现是设备默认开启了分页显示一屏显示满就停下来等你按空格。Netmiko虽然会自动翻页但翻页过程中如果遇到输出异常很大仍然可能截断。我的习惯是在执行命令前先通过send_command发送一条关分页的命令。华为设备是screen-length 0 temporary思科是terminal length 0。关掉分页后display current-configuration就能一次性输出完整内容。6.2 设备弹出交互确认脚本直接卡死有次跑批量脚本前两台设备正常第三台开始全部失败日志显示连接超时。后来手工连接才发现那台设备上某条命令会触发一个安全提示问是否继续脚本一直在等这个交互的回应直接卡到超时。现在我的原则是凡是可能触发交互确认的命令一律用send_command_timing并且在代码里明确处理交互分支。不要指望send_command能自动跳过所有确认它只处理命令回显不处理多轮对话。6.3 编码问题中文描述和特殊字符让脚本崩溃设备配置里如果有中文描述或者某些命令输出含有特殊字符Python默认的解码方式可能会直接报错。遇到这类问题连接参数里明确指定编码会更可靠device { device_type: huawei, host: 192.168.1.1, username: admin, password: password, encoding: utf-8, }同时写入文件时统一用encodingutf-8不要依赖系统默认编码。Windows控制台默认可能是gbk写到文件时很容易乱码。6.4 多设备并发时连接实例的线程安全批量设备数量多的时候一台台串行执行会比较慢。我试过用threading做并发但后来发现Netmiko的连接实例不是线程安全的直接在多个线程里共享同一个连接对象会出各种诡异问题。正确的做法是一台设备一个连接实例每个线程独立创建、独立销毁。简单并发示例from concurrent.futures import ThreadPoolExecutor def configure_device(dev): connection ConnectHandler(**dev) connection.send_config_set(commands) connection.disconnect() return dev[host], OK with ThreadPoolExecutor(max_workers5) as executor: futures [executor.submit(configure_device, dev) for dev in devices] for future in futures: print(future.result())并发数不建议开太大5到10个就足够了。设备侧还有CPU和SSH连接数上限并发太高反而容易触发设备的CPU过载保护。6.5 超时和无响应怎么兜底网络环境总有波动设备偶尔也会抽风。脚本里如果不对超时做兜底一台设备卡住就可能让整个任务挂起。Netmiko连接参数里的timeout、read_timeout、session_timeout建议全部显式设置不要依赖默认值。同时在批量脚本外层加一个总超时控制比如用func_timeout包住单台设备的执行函数超过3分钟强制终止并记录失败。兜底逻辑做扎实了自动化脚本才能真正做到跑了不用管。我自己的习惯是给每个批量任务加一个邮件或企微通知任务结束自动推送结果汇总失败设备单独列出第二天上班直接对着失败清单处理。从最开始用Paramiko一行一行地读设备输出到后来用Netmiko配合批量脚本和备份机制我对网络设备自动配置最大的体会是工具选型只是起步难点永远在设备差异、交互细节和流程设计上。刚开始可以不用追求一步到位先拿一台设备跑通抓取版本、执行一条简单配置再把范围慢慢扩大到批量任务。等脚本在你的环境里跑熟了你会发现它带来的不只是省时省力更是每一次变更都有记录、出问题能回滚的底气。
返回列表