简介:《基于Python实现网络自动化运维程序设计论文.docx》是一份完整的毕业设计论文文档,面向网络运维工程师、自动化运维学习者及高校网络工程专业学生。文档针对传统命令行运维方式错误率高、人力资源成本高的痛点,系统介绍了基于Python的自动化运维程序设计过程。内容涵盖网络运维现状分析、自动化运维的定义与分类、关键技术选型、华为ensp模拟器与FreeSSHd传输通道搭建、Python语法特点及主要模块应用,并给出了程序编写后的运行测试与结果分析。资源包共1个docx文件,大小1.86MB,包含中文摘要、英文摘要、目录及完整正文,结构规范,可作毕业论文写作模板,也可作为自动化运维项目落地的设计参考。目前已有117人学习,通过该文档可系统了解从环境搭建到脚本实现的完整链路,有效提升网络运维的灵活性与稳定性。
1. 网络自动化运维程序设计:把四十台设备的手工巡检交给 Python
每天早晨九点,运维都要登进几十台交换机逐台敲接口状态命令,核对端口是否 UP;晚上做版本变更,又得逐台上传配置并等待回显。基于 Python 实现网络自动化运维程序设计,本质就是把「登录、执行、解析、归档、告警」这条人工链路,拆成可重复运行的程序模块。它不只是省时间,更是把操作步骤固化下来,降低漏检和手误概率。这个方向适合网络工程师、运维开发,以及正在做课程设计或毕业论文的计算机相关专业学生,有一点 Python 基础语法知识就能跟上。
选 Python 而不是直接采购商业网管平台,原因很实际:现网设备几乎都支持 SSH 和 SNMP,而 Python 生态里既有 Paramiko 这样的底层 SSH 实现,也有 Netmiko 这类面向网络设备的封装库,还有 Nornir 负责批量任务的并发调度。写论文或设计文档时,这些库恰好能对应需求分析、总体设计、详细设计、测试验证的经典章节,程序结构和论文结构可以一一映射。
下面按一条既能答辩又能落地的路径来讲:先确认 Python 版本与依赖选型,再写出第一个能跑的最小脚本,然后补齐并发、重试、回滚这些真实运维才需要的能力,最后封装成带命令行参数和日志的调度工具。每节给出的命令和代码都可以直接复现。
2. 网络自动化运维的 Python 依赖选型与运行环境搭建
任何程序设计的第一步都是选型,网络自动化运维程序更是如此。它不是从零实现 SSH 协议,而是把成熟库按职责分层组合:Paramiko 负责最底层的 SSH 传输,Netmiko 负责把不同厂商的 CLI 差异封装成统一会话,Nornir 负责把批量执行抽象成带清单的并发任务。这一章先把这几层库的边界讲清楚,再给出可直接复现的环境搭建命令和设备连通性自检脚本。
2.1 Paramiko、Netmiko、Nornir 三层库各自的职责边界
先看最底层的 Paramiko。它是 SSHv2 协议的完整 Python 实现,负责连接建立、认证、通道管理,你的程序用exec_command就能拿到命令的 stdin、stdout、stderr 三个流。问题是网络设备的 CLI 和通用 Linux shell 有显著差异:命令回显超过一屏会停在-- More --,部分平台默认开启分页,进入特权模式还要单独发命令,慢速设备在命令后很久才重新出现提示符。这些差异如果都靠自己在 Paramiko 层手工处理,程序会充满 sleep 和字符串匹配,难以维护。
Netmiko 就是在 Paramiko 之上把这些差异收编掉的一层。它内置了几十个厂商平台配置,比如 cisco_ios、huawei、h3c、junos,连接时会自动关闭分页、进入特权模式、等待提示符,再让你用send_command一行代码拿到完整回显。需要强调的边界是:Netmiko 不负责任务编排,不负责多台设备的并发调度,它只解决「单台设备会话」的问题。Nornir 则正好补上编排这一层,它提供类似 Ansible 的 inventory 与任务插件机制,把多台设备的并行执行、结果汇总变成配置项而不是手写线程代码。
| 库 | 层级 | 典型用途 | 需要自己处理的坑 |
|---|---|---|---|
| Paramiko | SSH 传输层 | 自定义协议交互、SFTP 上传下载 | CLI 分页、提示符等待、超时控制 |
| Netmiko | 设备会话层 | 多厂商命令下发、配置备份 | 不同平台的错误提示格式、慢设备延时 |
| Nornir | 任务调度层 | 批量并行执行、结果汇总 | 任务结果序列化、并发时的连接数控制 |
除了 SSH 这条主线,还可以在论文的需求分析里补一句:新机型如果支持 NETCONF/RESTCONF,用 ncclient 或 requests 直接走结构化接口,程序连回显解析都省掉。多数现网存量设备仍以 SSH 为主,下面都以 SSH 为主实现。
2.2 用 venv 与 pip 搭建可复现的 Python 运行环境
环境搭建的目标是「任何人拿到 requirements.txt 都能复现」,这也是程序设计实践里容易被扣分的一环。先建虚拟环境再安装依赖,避免污染系统自带 Python,也不用为提权纠结。
python3 -m venv netauto source netauto/bin/activate python -m pip install --upgrade pip pip install paramiko netmiko nornir textfsm ntc-templates pip freeze > requirements.txt第一行命令用 venv 模块在当前位置创建名为 netauto 的隔离环境;第二行激活它,后续所有命令都在这个环境里执行。pip install安装的是五个核心依赖:paramiko 和 netmiko 用于 SSH 会话,nornir 用于并发编排,textfsm 与 ntc-templates 组合用于把回显解析成结构化数据,后者在后面的巡检解析里会用到。最后pip freeze把当前环境里所有包及其精确版本写入 requirements.txt,这是论文复现说明的标准做法。
安装完成后可以用pip list --format=columns | grep -E "paramiko|netmiko"确认版本号,再把requirements.txt提交到代码仓库或附在论文附录里。遇到公司内网无法直接访问 PyPI 的环境,常见做法是配置pip.conf指向内部镜像源,命令改为pip install -i http://mirror.internal/simple --trusted-host mirror.internal ...,这一步同样要写进环境说明。
2.3 设备连接参数表与最小连通性自检脚本
动手写巡检程序之前,先确定连接参数。下面这份参数表来自实际项目里的公共配置块,后面所有脚本都与它保持一致,尤其要注意 secret 与登录密码分开、device_type 必须与设备真实平台匹配这两条原则。
| 参数 | 作用 | 建议值 |
|---|---|---|
| device_type | 厂商平台类型 | cisco_ios / huawei / h3c / junos |
| host | 设备管理地址 | 管理网段 IP,不要用业务地址 |
| username / password | 登录凭据 | 独立运维账号,禁用手工个人账号 |
| secret | 特权模式密码 | 与登录密码分开配置 |
| port | SSH 端口 | 22,有跳板场景改为映射后的端口 |
| conn_timeout | TCP 连接超时 | 10 秒 |
| timeout | 单条命令整体超时 | 60~120 秒 |
| global_delay_factor | 全局延时放大系数 | 慢设备设为 2~3 |
确定参数后,先用一段最短的 Paramiko 脚本做连通性自检,验证账号、网络、设备 SSH 服务三条链路是否都正常。这个脚本也可以直接写进论文的单元测试部分。
import paramiko client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect( hostname="192.0.2.10", port=22, username="ops", password="******", timeout=10, look_for_keys=False, allow_agent=False, ) stdin, stdout, stderr = client.exec_command("display version | include VRP") print(stdout.read().decode("utf-8", errors="replace")) client.close()代码里的set_missing_host_key_policy让首次连接时自动接受主机密钥,适合测试环境;生产环境建议改为加载 known_hosts,避免中间人风险。look_for_keys=False防止程序误用本机 ssh key 去尝试认证,allow_agent=False则关掉 ssh-agent,确保只按代码里给的账号密码走。errors="replace"保证遇到非 UTF-8 回显(部分老设备默认字符集不同)时程序不会因解码异常中断。如果这台设备能打印出版本信息,说明环境与设备链路都已就绪,可以进入批量脚本的编写。
提示:自检脚本里的 AutoAddPolicy 只适合测试网段,生产环境应使用
load_host_keys加载已认证的主机密钥。
3. 用 Paramiko 与 Netmiko 编写批量巡检程序:三种命令执行方式
连通性验证通过后,下一步是写出第一个能产出文件的程序:读取设备清单,逐台登录,执行只读命令,把回显按设备名加时间戳归档。这一章先用 Paramiko 讲清两种底层交互方式的差异,再切换到 Netmiko 写实际可用的批量巡检脚本,最后给出按设备落盘的完整代码。
3.1 exec_command 与 invoke_shell:Paramiko 两种交互方式的取舍
Paramiko 的会话通道上命令执行有两种方式。exec_command是单发模式,程序把一整条命令交给远端,然后从 stdout 流里读取结果,适合执行完等输出、输出量可预期的命令。invoke_shell是交互模式,程序拿到一个 shell channel,自己控制发送内容和读取时机,适合需要连续应答、需要处理分页提示符的场景。两者取舍的关键在于:网络设备 CLI 默认分页,输出超过一屏会停顿,用exec_command读到的内容可能不完整,需要预先执行screen-length 0 temporary关分页;而invoke_shell需要自己维护状态,比如进入子菜单后按哪个键退出。
| 执行方式 | 适用场景 | 主要风险 |
|---|---|---|
| exec_command | 单条命令、一次性任务 | 分页截断、提示符未出现时读取超时 |
| invoke_shell | 交互式命令、进入子菜单 | 状态机复杂、时序依赖 sleep |
| send_command(Netmiko) | 日常巡检命令、配置查看 | 需正确设置 device_type 与延时 |
下面这段invoke_shell代码展示了关分页和读取回显的最简写法,理解它的时序问题,就能明白为什么批量程序最终选择 Netmiko。
import paramiko import time c = paramiko.SSHClient() c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) c.connect(hostname="192.0.2.10", username="ops", password="******", timeout=10) shell = c.invoke_shell() time.sleep(1) shell.recv(65535) # 先读掉登录后的 banner shell.send("screen-length 0 temporary\n") time.sleep(1) shell.recv(65535) shell.send("display interface brief\n") time.sleep(2) data = b"" while shell.recv_ready(): data += shell.recv(65535) print(data.decode("utf-8", errors="replace")) c.close()这个写法里三个time.sleep都是估算值:设备慢一点,1 秒可能读不全 banner;命令执行时间长一点,2 秒可能没等到完整输出。recv_ready循环只能把当前缓冲区内容取完,取不到还没到达的数据。这类时序脆弱性是底层直接用 Paramiko 写批量程序最大的痛点,所以实际项目里会引入 Netmiko,它内部用「等待指定提示符出现」代替固定 sleep。
3.2 Netmiko 的 send_command 参数与配置抓取写法
Netmiko 把上面那段交互逻辑封装成了send_command。连接时指定device_type,库内部自动完成关分页、等待提示符、超时清理这些动作。核心参数里值得记住的是read_timeout、expect_string和global_delay_factor:read_timeout是单次读取的最长等待,超时后返回已收集的字符,命令确实慢时宁可把值调大到 60 秒也不要设 10 秒后误判失败;expect_string用来指定命令结束的提示符特征,多数场景默认值就够;global_delay_factor是所有等待时长的放大系数,遇到老设备回显经常半截,就把它从 1 调到 2 或 3。
from netmiko import ConnectHandler device = { "device_type": "huawei", "host": "192.0.2.10", "username": "ops", "password": "******", "secret": "******", "timeout": 60, "global_delay_factor": 2, } conn = ConnectHandler(**device) conn.enable() output = conn.send_command("display interface brief", read_timeout=30) print(output) conn.disconnect()conn.enable()在 Cisco 平台对应进入特权模式,华为平台 Netmiko 的 huawei 配置档会做兼容处理,但保留secret字段仍是通用做法。配置抓取与普通巡检命令写法相同,把命令换成display current-configuration就是一次配置快照;Netmiko 还提供send_config_set接收配置行列表,send_config_from_file把整个配置文件逐行下发,这两个方法在后面做变更和回滚时会用到。需要提醒的是,send_command返回的是字符串,设备报错信息也在其中,程序必须自己检查返回内容里有没有Error、% Unknown这类关键词,而不是只看命令有没有执行完。
3.3 读取设备清单做批量巡检并逐台落盘
有了单台设备的稳定会话,批量只是多一层文件读取和循环。设备清单用纯文本devices.txt,每行一个设备,格式为IP,device_type,以#开头的行作为注释跳过。巡检命令放进一个列表,集中定义的好处是以后加命令只改一处。密码不建议硬编码,常见做法是从环境变量或独立配置文件读取,脚本里保留占位符即可。
from netmiko import ConnectHandler from datetime import datetime from pathlib import Path OUT = Path("inspection_output") OUT.mkdir(exist_ok=True) commands = ["display version", "display interface brief"] stamp = datetime.now().strftime("%Y%m%d_%H%M%S") with open("devices.txt", "r", encoding="utf-8") as f: lines = [line.strip() for line in f if line.strip() and not line.startswith("#")] for line in lines: host, dev_type = line.split(",") try: with ConnectHandler( device_type=dev_type, host=host, username="ops", password="******", timeout=60, ) as conn: out_path = OUT / f"{host}_{stamp}.txt" with out_path.open("w", encoding="utf-8") as fout: for cmd in commands: fout.write(f"===== {cmd} =====\n") fout.write(conn.send_command(cmd, read_timeout=30)) fout.write("\n") print(f"[OK] {host} -> {out_path}") except Exception as exc: print(f"[FAIL] {host}: {exc}")文件读取统一用encoding="utf-8",设备清单和输出文件保持同一种编码,避免脚本在 Windows 和 Linux 之间搬运时出现乱码。with ConnectHandler(...) as conn确保无论命令成功与否连接都会被关闭,这是网络自动化运维程序设计里最重要的资源管理习惯。输出文件名带时间戳,同一设备多次巡检不会互相覆盖。这里先按设备串行执行,逻辑清晰但耗时随设备数线性增长,且单台设备认证失败会拖慢整个批次,下一章把它改成并发并加重试。
4. 网络自动化运维程序的并发、重试与回滚设计
一个能演示的程序和一款能上线用的程序,差别就在失败路径怎么处理。串行巡检脚本在设备数量少时没有问题,设备到了几十台,单台卡死会拖垮整个批次;命令执行偶发超时,程序直接中断又需要人工重启。这一章把并发、重试、回滚三个机制逐个补上,每一块都是一个独立设计单元,可以直接对应论文里的详细设计小节。
4.1 ThreadPoolExecutor 并发巡检:max_workers 怎么定才不压垮设备
SSH 会话是典型的 IO 密集型操作,等待网络往返时 CPU 基本空闲,所以用线程池而不是进程池。Python 的concurrent.futures.ThreadPoolExecutor用起来开销最小,线程数量即max_workers是从connect到disconnect同时进行的会话数。这个值不能只按「越大越快」来定,设备端 SSH 会话数有上限,登录并发过高会触发设备 CPU 飙升甚至临时拒绝连接。常见做法是按设备总数折中:10 台以内用 3~5 个并发,10~50 台用 8~12 个,50 台以上不超过 24 个,同时把一批拆成几个批次跑,每批之间休息 30 秒,让设备有恢复时间。
| 设备数量范围 | 建议 max_workers | 补充说明 |
|---|---|---|
| 1~10 台 | 3~5 | 并发收益不明显,优先保证日志完整 |
| 10~50 台 | 8~12 | 常见生产规模,单批 3~5 分钟量级 |
| 50~200 台 | 16~24 | 必须分批,批间隔 30 秒以上 |
| 200 台以上 | 24 封顶 | 改用 Nornir 或按地域分片调度 |
用as_completed处理结果的好处是:哪台先返回就先处理哪台,不会因为某台慢设备卡住后面所有设备的汇总。
from concurrent.futures import ThreadPoolExecutor, as_completed def inspect_one(line): host, dev_type = line.split(",") with ConnectHandler( device_type=dev_type, host=host, username="ops", password="******", timeout=60, ) as conn: version = conn.send_command("display version", read_timeout=30) return host, True, version results = [] with ThreadPoolExecutor(max_workers=10) as pool: futures = {pool.submit(inspect_one, line): line for line in lines} for fut in as_completed(futures): host, ok, payload = fut.result() results.append((host, ok, payload))futures字典把 future 对象和原始设备行对应起来,as_completed遍历时能知道返回结果属于哪台设备。fut.result()在任务内部抛出异常时会在这里重新抛出,所以inspect_one内部要做好异常收集,让单台失败不影响整批。并发下要格外注意输出文件命名不能冲突,文件名里的时间戳要在提交任务前统一生成,而不是在任务内部各自取当前时间。
注意:max_workers 不是越大越好,超出设备 SSH 会话上限会让整批巡检在 connect 阶段集体失败。
4.2 带指数退避的重试与只读任务的幂等设计
网络命令失败的原因分两类:一类是连接层抖动、设备临时繁忙,重试能恢复;另一类是认证失败、参数写错,重试多少次都一样。重试设计的第一步就是区分这两类。认证失败要在进入重试循环前单独捕获并直接标记失败,避免把系统错误当临时故障反复撞设备。临时故障用指数退避,第一次等 2 秒,第二次 4 秒,让设备有时间恢复,也避免所有线程同时重试造成的重试风暴。
import time from functools import wraps def retry(times=3, wait=2.0): """命令执行失败后按 2 秒、4 秒退避重试。""" def decorator(fn): @wraps(fn) def wrapper(*args, **kwargs): last_exc = None for attempt in range(times): try: return fn(*args, **kwargs) except Exception as exc: last_exc = exc if attempt < times - 1: time.sleep(wait * (2 ** attempt)) raise last_exc return wrapper return decorator装饰器用functools.wraps保留原函数元信息,这样并发提交时任务名仍然可读。times=3, wait=2.0是默认值,实际调用时可通过参数覆盖,使用时直接@retry(times=3, wait=2.0)装饰inspect_one这类函数。只读巡检命令天然幂等,重复执行不会改变设备状态,可以放心套这个重试;配置变更命令则不推荐盲目重试,因为第二次执行可能恰好落在变更已生效、回显模式与原预期不同的状态上。幂等设计的常见做法是「先查后改」:下发前先执行查看命令确认目标配置是否已存在,存在就跳过,不存在才下发,并且把每一步的回显记录到日志,方便事后核对。
4.3 变更前快照与自动回滚的最小实现
配置变更程序必须回答一个问题:改错了怎么办。最简单可靠的答案是在变更前把当前配置完整保存下来,变更后做关键词校验,校验不过就用保存的配置恢复。这段逻辑是「备份、变更、验证、回滚」四步闭环,也是网络自动化运维论文里最值得展开设计的一个环节。
from datetime import datetime from pathlib import Path BACKUP_DIR = Path("config_backup") BACKUP_DIR.mkdir(exist_ok=True) def snapshot_config(conn): stamp = datetime.now().strftime("%Y%m%d_%H%M%S") content = conn.send_command("display current-configuration", read_timeout=60) path = BACKUP_DIR / f"{conn.host}_{stamp}.cfg" path.write_text(content, encoding="utf-8") return path def apply_and_verify(conn, config_lines, expect_keyword): conn.send_config_set(config_lines) result = conn.send_command( "display current-configuration | include " + expect_keyword, read_timeout=30, ) return expect_keyword in resultsnapshot_config在变更发起前调用,文件名带设备名和时间戳,多个变更周期不会互相覆盖。apply_and_verify先send_config_set下发配置行,再用include过滤查看目标配置是否已经生效,返回布尔值。主流程里只要判断这个布尔值,为 False 就执行conn.send_config_from_file(str(backup_file))回滚。这里有两个现实提醒:整份配置回滚在设备上相当于全量下发,部分平台会中断在线会话,批处理场景建议逐台变更、逐台验证;华为设备回滚后用save持久化,Cisco 平台对应write memory,这两条命令本身就是变更程序中除了业务配置之外必须考虑的动作。
5. 用命令行参数与 TextFSM 把巡检程序封装成可调度运维工具
程序功能齐全之后,最后一步是让它能交给定时任务和别的同事使用。封装的核心是两件事:运行参数不写死在代码里,输出结果从字符串变成结构化数据。
5.1 用 argparse 定义巡检命令与 --dry-run 预演
import argparse parser = argparse.ArgumentParser(description="网络自动化运维巡检工具") parser.add_argument("--inventory", default="devices.txt", help="设备清单,每行 IP,device_type") parser.add_argument("--commands", nargs="+", default=["display version"], help="要执行的命令列表") parser.add_argument("--workers", type=int, default=10, help="并发线程数") parser.add_argument("--dry-run", action="store_true", help="只打印执行计划,不建立 SSH 连接") args = parser.parse_args()--dry-run是这个工具最便宜的一道保险:定时任务配置出错、设备列表被误改时,先空跑一遍看打印出来的设备与命令是否符合预期。实跑前先执行python inspect.py --inventory devices.txt --dry-run,确认无误再去掉该选项。
5.2 用 use_textfsm 把回显解析成结构化 CSV
Netmiko 的send_command支持use_textfsm=True,会自动到 ntc-templates 里查找对应厂商和命令的模板,把回显解析成字典列表。以接口状态为例,解析后每行是一个接口,字段就是模板里定义的列名。
import pandas as pd records = conn.send_command( "display interface brief", use_textfsm=True, ) df = pd.DataFrame(records) df.to_csv("interfaces.csv", index=False, encoding="utf-8-sig")encoding="utf-8-sig"这个细节值得记住:生成的 CSV 用 Excel 打开时,带 BOM 头才不会乱码。模板库里没有对应命令时,send_command会退回返回原始字符串,所以代码里要判断返回值是 list 还是 str,前者说明解析成功,后者说明只拿到了原始回显。
5.3 接入 cron 前的四项自检清单
把工具交给定时任务前,至少确认四件事:用--dry-run跑通执行计划;挑 3~5 台设备实跑,核对输出文件数量与设备数一致;检查 CSV 里端口状态列的取值全部落在预期枚举内;脚本最后用sys.exit(0)和sys.exit(1)区分成功与失败,这样 cron 或监控系统能靠退出码感知故障。调度采用系统 crontab,形如:
30 8 * * * cd /opt/netauto && .venv/bin/python inspect.py --inventory devices.txt --workers 10 >> run.log 2>&1注意 crontab 里要用虚拟环境的绝对路径.venv/bin/python,而不是python,因为定时任务的环境变量里通常没有激活 venv。如果巡检结果交给下游自动化平台消费,把 CSV 路径和退出码一起写到run.log,下游任务只认日志里最后一行状态。遇到个别老设备回显被截断时,优先把该设备的global_delay_factor调到 3,并在send_command里显式指定expect_string为设备真实提示符后再次验证输出完整性。
本文还有配套的精品资源,点击获取