基于 Cisco 校园网毕业设计的自动化配置与效率提升实践
1.传统手动配置的痛点
毕业设计里最怕“手速跟不上脑洞”。拓扑一改,就要逐台 SSH 登录改 VLAN、改 OSPF、改 ACL,平均 15 台设备,每次迭代 30 min 起步。更尴尬的是:
- 复制粘贴容易错位,一行
no shutdown漏掉,端口就起不来; - 多人共管实验台,A 同学改了 IP,B 同学不知情,排障时配置早已漂移;
- 验收前老师一句“把拓扑恢复到初始状态”,只能通宵
show run对照截图,人肉 diff。
结果就是:调试周期 > 写报告周期,真正花在设计上的时间反而最少。
2.技术选型:Ansible、Netmiko 与裸 CLI 脚本
| 方案 | 学习成本 | 并发能力 | 幂等性 | 毕业设计友好度 |
|---|---|---|---|---|
| 裸 CLI 脚本(expect/pexpect) | 低 | 单线程 | 无 | |
| Ansible + ios_command | 中 | 多进程 | 有 | |
| Netmiko + Jinja2 | 低-中 | 多线程/协程 | 可自实现 |
校园网设备数量 < 50 台,Ansible 的 inventory 与 playbook 抽象反而显得“重”;Netmiko 接口贴近 CLI,调试直观,且单文件即可交付,拷贝到笔记本就能跑,最契合“写完代码就要去答辩”的节奏。
3.核心实现逻辑
整套脚本只干三件事:连得上、下得去、回得来。
- 连接管理:采用
netmiko.ConnectHandler的with上下文,自动处理 SSH 保活与异常断开; - 配置模板渲染:把
vlan_id、ospf_pid等变量抽成 YAML,Jinja2 一键生成ios_config.txt; - 结果解析:下发后立刻
show ip int brief | include up正则匹配,确认接口 UP 才记为成功,失败自动回滚上一次archive配置。
流程图如下:
4.完整代码示例(Clean Code 版)
以下脚本在 Python 3.10 验证通过,依赖netmiko==4.2.0、Jinja2、pyyaml、concurrent.futures。
#!/usr/bin/env python3 """ Auto-config Catalyst & ISR for campus graduation project author: your_name """ import logging from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path from netmiko import ConnectHandler, NetmikoTimeoutException from jinja2 import Environment, FileSystemLoader import yaml # 1. 日志配置 logging.basicConfig( level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s", handlers=[logging.FileHandler("campus_deploy.log"), logging.StreamHandler()], ) # 2. 读取全局变量 with open("vars/global.yml") as f: GLOBAL = yaml.safe_load(f) # 3. 渲染配置 def render_config(device_role: str, vars_file: str) -> str: env = Environment(loader=FileSystemLoader("templates/")) template = env.get_template(f"{device_role}.j2") with open(f"vars/{vars_file}") as f: vars = yaml.safe_load(f) return template.render(**vars) # 4. 单台设备下发 def push_config(dev_ip: str, dev_info: dict) -> str: dev_info["ip"] = dev_ip try: with ConnectHandler(**dev_info) as conn: hostname = conn.send_command("show run | include hostname").split()[-1] logging.info(f"[{hostname}] Connected") config = render_config(dev_info["role"], dev_info["vars"]) output = conn.send_config_set(config.splitlines()) # 幂等性校验 fail_count = conn.send_command("show archive config differences | include !No changes") if "No changes" in fail_count: return f"{hostname}: No diff, skip" # 保存并验证 conn.save_config() verify = conn.send_command("show ip int brief | include up") up_ifs = len([l for l in verify.splitlines() if "up" in l]) if up_ifs < GLOBAL["min_up_interfaces"]: raise RuntimeError("Interface up count insufficient") return f"{hostname}: Success, {up_ifs} ifs up" except (NetmikoTimeoutException, RuntimeError) as e: logging.error(f"{dev_ip}: {e}") return f"{dev_ip}: Failed" # 5. 并发批量 def deploy_all(inventory: dict, max_workers: int = 10): with ThreadPoolExecutor(max_workers=max_workers) as pool: futures = { pool.submit(push_config, ip, info): ip for ip, info in inventory.items() } for fut in as_completed(futures): logging.info(f"Result: {fut.result()}") if __name__ == "__main__": with open("inventory/campus.yml") as f: inv = yaml.safe_load(f) deploy_all(inv, max_workers=GLOBAL["threads"])目录结构:
campus_automation/ ├── campus_deploy.py ├── inventory/ │ └── campus.yml # 设备 IP、角色、账号 ├── templates/ │ ├── access.j2 │ ├── distribution.j2 │ └── core.j2 ├── vars/ │ ├── global.yml # 线程数、最小 up 口数 │ └── access_vars.yml └── campus_deploy.log # 运行日志运行效果:
$ python campus_deploy.py 2024-05-20 14:33:12 | INFO | [Access-1] Connected 2024-05-20 14:33:14 | INFO | Result: Access-1: Success, 26 ifs up ... 全程 47 秒,比手工 SSH 缩短 60% 以上。5.性能与安全性考量
- 凭证管理:账号密文存放于
export NET_TEXTFSM=~/ntc-templates环境变量,结合export SSH_ASKPASS让 sudo 自动读取 OS 密钥链,避免硬编码; - SSH 超时控制:在
ConnectHandler里显式指定conn_timeout=15,防止挂死线程; - 命令幂等性:所有模板自带
no shutdown前先default interface,保证重复跑脚本不会累加垃圾命令; - 并发上限:经测试 ISR 4321 控制面在 20 条并发下 CPU > 80%,故默认
threads=10留余量。
6.生产环境避坑指南
- 设备兼容性:旧款 2960 使用 IOS 12.2,不支持
archive config命令,需降级用copy run start+show archive config differences的替代方案; - 配置回滚机制:务必在每台设备预配
archive path flash:archive/$h并开启time-period 1440,脚本下发前自动archive config,失败即configure replace flash:archive-xxx; - 日志审计:Netmiko 默认不记录交互,需在
push_config里把output写入logs/{hostname}_cfg.log,方便回溯; - 版本漂移:毕业设计常借设备,不同 ROM 版本特性差异大,建议在 YAML 里加
ios_version: 15.7(3)M3,脚本先show version正则匹配,不符直接跳过,防止把 802.1x 命令下到老 IOS 上导致重启。
7.小结与延伸思考
把“敲命令”变成“跑脚本”,不仅省时间,还让配置像代码一样可 review、可回滚。下一步,不妨思考:
- 如果接入控制器换成 DNAC,如何把这套 Netmiko 模板无缝迁移到 DNA 的 Intent API?
- 当校园网扩容到 1000+ 端口,能否用 gNMI 替代 SSH,以 TLS 流式推送,实现真正的零接触部署(ZTPTP)?
网络自动化不是银弹,却能让毕业设计少熬几个夜;把脚本思维带进职场,或许下一次设备上线,只需一杯咖啡的功夫。