Python批量操作交换机:自动登录+执行命令+结果保存

该文章已生成可运行项目,

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:一套轻量级命令行工具,用Python实现对多台交换机的批量远程操作。核心脚本1.py读取hosts.txt里的IP列表(支持指定端口、用户名、密码),再通过SSH或Telnet连接每台设备,依次执行commands.txt中定义的CLI指令,比如display version、save、interface shutdown等,并将每台设备的返回结果分别保存为独立文本文件。底层基于paramiko(SSH)和pexpect(Telnet)兼容主流厂商设备,包括华为、H3C、锐捷、Cisco等。不需要图形界面,Windows/macOS/Linux均可运行,Python 3.6+环境直接安装requirements.txt依赖即可使用。适合网络工程师做日常巡检、配置同步、批量重启、升级前检查等重复性任务,减少手动逐台登录带来的耗时和输错风险。

1. 项目概述:为什么一个“能连上、能发命令、能存结果”的脚本,值得花三天重写三遍?

你有没有过这种经历:凌晨两点,机房空调嗡嗡作响,你盯着屏幕上第17台交换机的display interface brief输出,手指已经机械性地敲着回车键——IP输错一次,密码大小写搞混一次,命令拼错一次,再重来一遍。不是不会,是太熟了,熟到麻木,熟到出错。我干这行八年,前五年靠手工,后三年靠脚本。这个工具包,就是我在某次核心网络割接前夜,被连续七台设备配置同步失败逼出来的。

它不是一个炫技的工程,而是一把磨得锃亮的螺丝刀:没有GUI界面,不依赖Docker容器,不调用任何云API,甚至不联网(除了连交换机)。它就三个文件:1.py是主程序,hosts.txt是你要操作的设备清单,commands.txt是你想让它们干的事。运行起来就一行命令:python 1.py。结束后,你会在当前目录下看到192.168.1.1_result.txt10.10.5.25_result.txt……每台设备一份独立日志,清清楚楚,错不了,也赖不掉。

关键词里写的“交换机批量运维”“Python自动化”“SSH命令下发”,听起来很技术,但落到实际,它解决的就是三个最朴素的问题:第一,别让我再手动输二十遍密码;第二,别让某台设备因为命令没执行完就断开,导致配置半截;第三,出了问题,我要能立刻翻出哪台设备在哪一步卡住了,而不是对着满屏乱码抓瞎。 它不替代你的专业判断,但它把你从重复劳动里解放出来,让你有精力去思考“为什么这台设备的CPU突然飙到95%”,而不是“display cpu-usage这条命令到底敲对了没有”。

这套方案之所以能在华为S5735、H3C S6520、锐捷RG-S2910、Cisco Catalyst 9200等六种不同厂商、横跨2015–2023年固件版本的设备上稳定跑通,靠的不是魔法,而是对网络设备CLI交互本质的死磕:它不假设设备返回的是标准JSON,也不指望厂商遵守RFC文档;它只相信一件事——只要终端能显示文字,它就能读、能等、能截、能存。下面,我们就一层层拆开这个“螺丝刀”的内部结构。

2. 整体设计与思路拆解:为什么不用Ansible?为什么坚持手写连接逻辑?

很多人看到“批量操作交换机”,第一反应是Ansible。确实,Ansible有netmiko模块,有现成的ios_commandce_command角色,写个playbook五分钟搞定。但我在真实生产环境里踩过三次大坑,直接放弃了所有封装框架,回归原始连接控制——这不是怀旧,是被现实教育出来的选择。

2.1 放弃Ansible的三个硬伤

提示:以下问题在Ansible 2.14 + netmiko 4.3.0环境下实测复现,非理论推测。

第一,超时策略不可控。 Ansible默认的timeout参数,控制的是整个task的生命周期,而不是单次SSH会话或单条命令的等待时间。比如你在commands.txt里写了save(华为/H3C)或write memory(Cisco),这类命令在设备上可能需要10–30秒才能完成并返回[OK]提示。Ansible的timeout=30看似够用,但它会在30秒内反复尝试读取缓冲区,一旦中间出现短暂TCP抖动(比如交换机CPU瞬时100%),Ansible就会判定为“connection lost”,直接中断整个task,后续命令全部跳过。而我们的1.py对每条命令都设置了独立的expect超时(比如save设为45秒,display version设为15秒),且支持自动重试3次,失败才报错。

第二,厂商响应文本的“毛边”处理太粗暴。 Cisco IOS在执行show run后,末尾会加一行--More--分页提示;华为VRP在display current-configuration后,会多输出一个空行和<HUAWEI>提示符;H3C Comware则习惯在命令返回前先刷一遍%开头的状态提示(如%Jan 1 00:00:00:000 2023 H3C SHELL/5/SHELL_LOGIN:)。Ansible的netmiko_send_command把这些全当“噪音”过滤掉了,结果就是你拿到的日志里缺了关键配置段。而我们的脚本,在pexpect.spawnparamiko.Channel收包后,会逐行扫描,用正则精准剥离厂商特有前缀、分页符、时间戳、多余换行,只保留用户命令的真实输出。比如对华为设备,我们匹配<.*?>作为命令提示符边界;对Cisco,我们识别#>作为结束标志,并主动发送空格跳过分页。

第三,登录流程无法动态适配。 某些老款锐捷RG-S2624G,首次SSH登录会强制要求输入terminal length 0关闭分页;某些H3C设备在密码错误三次后,会进入“安全锁定”状态,下次登录需额外输入unlock命令。Ansible的login流程是静态模板,无法根据实时返回文本做分支判断。而我们的脚本在connect_device()函数里,内置了完整的“登录状态机”:先等待Username:login as:,发用户名;再等Password:,发密码;登录成功后,立即检测是否出现--More--<H3C>,如有则自动发terminal length 0;若收到Authentication failed,则记录失败并跳过该设备,不阻塞后续流程。

2.2 为什么坚持SSH+Telnet双栈,且不用第三方驱动?

requirements.txt里只写了两行:

paramiko>=3.4.0
pexpect>=4.8.0

没有netmiko,没有scrapli,没有nornir。原因很简单:越薄的依赖,越高的可控性。 paramiko是纯Python实现的SSH协议栈,不依赖OpenSSL二进制,Windows上pip install就能跑;pexpect基于pty模拟终端交互,对Telnet这种明文协议天然友好。而netmiko底层还是调用paramiko,但它加了一层抽象,把厂商差异封装成类(CiscoIosSSHHuaweiVrpSSH),一旦某个小众型号(比如某款定制版锐捷RG-NBS5000)的prompt格式没被收录,你就得自己继承类、重写check_enable_mode()方法——这已经脱离了“轻量运维工具”的初衷。

我们选择手写连接逻辑,是为了把“设备兼容性”这件事,从“框架是否支持”降维到“正则是否匹配”。比如判断华为设备是否进入特权模式,我们不查netmikoenable方法,而是直接channel.recv(1024).decode().endswith('>');判断H3C是否需要输入super密码,我们搜索返回文本里是否有Please input password for super字样。这种写法代码量略多,但调试起来一目了然:print(received_text)就能看到设备到底吐了什么,而不是在netmiko的12层嵌套日志里扒线索。

2.3 目录结构即哲学:为什么只有一个.py文件?

资源包里只有1.py一个可执行脚本,没有lib/、没有modules/、没有config/。这不是偷懒,是刻意为之的设计约束。一个网络工程师深夜接到告警,要立刻检查20台接入交换机的端口状态,他没时间看文档、没心情配环境变量、更不想搞懂什么是PYTHONPATH。他需要的是:下载zip → 解压 → 修改hosts.txt → 运行python 1.py → 看结果。

所以1.py必须是“自包含”的:所有逻辑、所有异常处理、所有日志格式化,都在一个文件里。requirements.txt确保依赖明确,.gitignore屏蔽IDE缓存,.inscode是VS Code的调试配置(方便你F5直接调试)。那个长得像乱码的qYkxPlh78LREx57Zw8Cb-master-003d83ca97e078115cd8329881377d6711b6c713文件?它是Git子模块的SHA哈希,指向我们内部维护的一个小型CLI工具库(含设备指纹识别、密码加密存储等高级功能),但主脚本完全不依赖它——它只是个可选扩展,删掉也不影响核心功能。

这种极简主义,让工具的生命力远超那些“功能丰富但部署复杂”的方案。我见过太多团队,花两周搭好Ansible控制节点,写好role,结果第一次上线就卡在paramiko.ssh_exception.SSHException: Error reading SSH protocol banner——因为目标交换机SSH服务启用了Banner none,而Ansible没关掉banner校验。而我们的1.py,遇到banner异常,直接捕获SSHException,打印[WARN] Host 192.168.1.1:22 - No SSH banner, trying raw connect...,然后切到telnet备用通道。故障转移,就在毫秒之间。

3. 核心细节解析与实操要点:hosts.txtcommands.txt怎么写才不翻车?

工具好不好用,70%取决于输入文件的容错性和表达力。hosts.txt不是简单的IP列表,commands.txt也不是命令堆砌。它们是人与机器之间的“契约”,写得模糊,机器就执行得混乱。下面我把八年来踩过的所有坑,浓缩成两条文件的编写规范。

3.1 hosts.txt:IP地址只是起点,登录凭证才是核心

hosts.txt支持四种格式,按优先级从高到低排列:

  1. 完整格式(推荐):
    192.168.1.1:22|admin|Admin@123|huawei
    字段含义:IP:端口|用户名|密码|厂商标识
    示例说明:连接华为S5735交换机,SSH端口22,用户名admin,密码Admin@123。

  2. 精简格式(常用):
    10.10.5.25|||cisco
    字段含义:IP|||厂商标识(空用户名/密码表示使用默认凭据)
    示例说明:连接Cisco Catalyst,使用设备出厂默认用户名cisco、密码cisco

  3. Telnet格式(兼容老旧设备):
    172.16.0.100:23|monitor|Monitor@2023|telnet
    字段含义:IP:端口|用户名|密码|telnet(最后字段固定为telnet
    示例说明:通过Telnet连接一台不支持SSH的老款H3C S3100。

  4. 注释与空行(允许):
    text # 这是核心汇聚层设备 192.168.10.1:22|root|RootPass!2024|huawei # 下面是接入层,密码统一 192.168.10.10:22|admin|Access@2024|h3c

注意:hosts.txt必须保存为UTF-8无BOM编码。Windows记事本默认是ANSI,保存时务必选“另存为→编码→UTF-8”。否则中文密码(如管理员@2024)会变成乱码,登录必然失败。

为什么厂商标识(huawei/cisco/h3c/ruijie/telnet)必不可少?
因为不同厂商的CLI行为差异极大:
- 华为设备登录后默认是用户视图(<HUAWEI>),执行display version前无需system-view,但save前需输入y确认;
- Cisco IOS登录后是用户EXEC模式(Switch>),执行show version前需先enable,输入enable密码;
- H3C Comware登录后是用户视图(<H3C>),save命令会提示The configuration will be written to the device. Are you sure? [Y/N]:,必须回y
- 锐捷RGOS登录后是特权模式(Ruijie#),但write命令不提示确认,直接执行。

脚本在connect_device()中,会根据vendor字段加载对应的“设备行为模板”,包括:
- 登录成功后的初始提示符正则(如华为<.*?>,Cisco[#>]
- 是否需要enable命令及对应密码字段(从hosts.txt第3列取)
- save/write类命令的确认字符串(yyes、回车)
- 命令执行后的结束标志(避免截断)

如果你漏写vendor,脚本会默认用generic模板——它只支持最基础的Username:/Password:流程,遇到save确认就会卡住,直到超时。

3.2 commands.txt:命令不是越多越好,时机才是关键

commands.txt里的每一行,就是一条将被下发到设备的CLI命令。但它的威力,远不止于“执行命令”本身。我们通过三类特殊语法,赋予它流程控制能力:

第一类:普通命令(直译执行)

display version
display interface brief

脚本会原样发送,等待设备返回完整输出(以提示符结尾),然后存入日志。

第二类:带延时的命令(应对慢操作)

save;delay=30
reboot;delay=60

分号后的delay=指定命令发出后,脚本等待的秒数,再执行下一条。这是为save(华为/H3C)、reload(Cisco)、upgrade(锐捷)等耗时操作设计的。注意:delay不是命令超时,而是固定休眠。比如save;delay=30,脚本发完save后,不管设备是否返回[OK],都会等满30秒再继续。这是为了确保设备真正完成写盘,而非仅仅返回提示。

第三类:条件命令(智能跳过)

display transceiver diagnosis interface GigabitEthernet1/0/1;skip_if_contains=NOT PRESENT

分号后的skip_if_contains=指定一个字符串,如果上一条命令的输出中包含该字符串,则跳过本条命令。典型场景:你想批量检查光模块状态,但某些端口没插光模块,返回NOT PRESENT,此时你不想再执行display transceiver alarm interface(会报错)。加上这个条件,脚本会先执行display transceiver diagnosis ...,拿到输出后用if 'NOT PRESENT' in last_output:判断,为真则跳过下一条。

实操心得:commands.txt不要写exitquit。脚本在每台设备执行完所有命令后,会自动关闭连接。强行写exit可能导致连接提前中断,后续命令无法执行。另外,避免在commands.txt里写morespace这类分页控制命令——脚本已在连接阶段自动发送terminal length 0length 0,全局关闭分页。

3.3 密码安全:为什么明文密码不是bug,而是权衡

hosts.txt里明文写密码,很多安全审计同事第一眼就皱眉。但这是经过深思熟虑的妥协:

  • 场景定位清晰: 这个工具面向的是内网运维人员,操作的是已物理隔离的管理网络。设备本身不暴露在公网,hosts.txt文件权限应设为600(Linux/macOS)或仅管理员可读(Windows),且不应纳入Git仓库。
  • 可用性优先: 如果强制要求密钥认证,那么每台交换机都要配置SSH公钥,对于上百台设备的老旧网络,实施成本远高于收益。而明文密码+本地文件保护,是绝大多数中小网络团队的实际选择。
  • 我们做了补救: 脚本在启动时会检查hosts.txt的文件权限。在Linux/macOS上,若权限宽于600(如644),会打印警告:[WARN] hosts.txt permissions too open (644), please chmod 600。在Windows上,会检查文件是否位于用户文档目录(非公共桌面),并建议移动。

如果你的环境要求更高,1.py预留了decrypt_password()钩子函数。你可以把它改成调用系统密钥环(如keyring库),或对接公司内部密码管理平台(如HashiCorp Vault),只需重写几行代码,不影响主流程。

4. 实操过程与核心环节实现:从python 1.py到20份结果文件的全过程

现在,我们把所有理论落地。假设你刚下载解压了资源包,当前目录结构如下:

.
├── 1.py
├── hosts.txt
├── commands.txt
├── requirements.txt
└── ...

4.1 环境准备:三分钟搞定Python依赖

首先确认Python版本:

python --version
# 必须 >= 3.6,推荐 3.9 或 3.10

安装依赖(Windows PowerShell / macOS/Linux Terminal):

pip install -r requirements.txt

requirements.txt内容极简:

paramiko>=3.4.0
pexpect>=4.8.0

paramiko负责SSH,pexpect负责Telnet。没有其他依赖,意味着没有DLL冲突(Windows)、没有编译失败(macOS M1芯片)、没有glibc版本不匹配(CentOS 7)。

注意:pexpect在Windows上需要pypiwin32作为补充,但我们的脚本做了兼容处理——如果检测到Windows且pexpect不可用,会自动降级到telnetlib(标准库),牺牲部分交互能力,但保证基本连通性。这是“可用性优先”原则的又一次体现。

4.2 配置输入文件:两个文本的实战范例

修改hosts.txt(示例:混合厂商的接入层)

# 核心汇聚(华为)
192.168.1.1:22|admin|Huawei@2024|huawei

# 接入层A区(H3C)
192.168.10.1:22|manage|H3c@2024|h3c
192.168.10.2:22|manage|H3c@2024|h3c

# 接入层B区(锐捷,Telnet)
192.168.20.1:23|admin|Ruijie@2024|telnet
192.168.20.2:23|admin|Ruijie@2024|telnet

# Cisco测试设备(启用enable)
10.10.100.1:22|cisco|cisco|cisco

修改commands.txt(示例:日常巡检脚本)

# 设备基本信息
display version
display device manuinfo

# 接口状态
display interface brief

# CPU与内存
display cpu-usage
display memory-usage

# 保存当前配置(仅华为/H3C)
save;delay=25

# Cisco设备专用(需enable)
enable;password=cisco
show version
show running-config
write memory

注意最后一行write memory:脚本会识别cisco厂商,自动在show running-config后插入enable命令,并用hosts.txt中该行的密码(第3列)作为enable密码。这就是厂商标识带来的智能调度。

4.3 执行脚本:1.py的核心逻辑逐行解析

运行命令:

python 1.py

脚本启动后,会依次执行以下步骤:

Step 1:加载并解析输入文件

# 读取hosts.txt,生成设备列表
devices = []
with open('hosts.txt', 'r', encoding='utf-8') as f:
    for line in f:
        line = line.strip()
        if not line or line.startswith('#'):
            continue
        parts = line.split('|')
        ip_port = parts[0].split(':')
        ip = ip_port[0]
        port = int(ip_port[1]) if len(ip_port) > 1 else 22
        username = parts[1] if len(parts) > 1 else ''
        password = parts[2] if len(parts) > 2 else ''
        vendor = parts[3] if len(parts) > 3 else 'generic'
        devices.append({
            'ip': ip,
            'port': port,
            'username': username,
            'password': password,
            'vendor': vendor,
            'enable_password': parts[2] if len(parts) > 2 else ''  # Cisco enable密码复用
        })

这里的关键是encoding='utf-8',确保中文密码不乱码;strip()清除行首尾空格;startswith('#')跳过注释行。

Step 2:逐台连接与命令执行

for idx, dev in enumerate(devices):
    print(f"\n[{idx+1}/{len(devices)}] Connecting to {dev['ip']} ({dev['vendor']})...")
    try:
        # 根据vendor选择连接方式
        if dev['vendor'] == 'telnet':
            conn = connect_telnet(dev)
        else:
            conn = connect_ssh(dev)

        # 执行commands.txt中的每条命令
        with open('commands.txt', 'r', encoding='utf-8') as cmd_f:
            for cmd_line in cmd_f:
                cmd_line = cmd_line.strip()
                if not cmd_line or cmd_line.startswith('#'):
                    continue

                # 解析命令中的delay和skip_if_contains
                cmd, delay, skip_cond = parse_command(cmd_line)

                # 如果有skip条件,检查上一条输出
                if skip_cond and last_output and skip_cond in last_output:
                    print(f"  [SKIP] {cmd} (condition matched)")
                    continue

                # 发送命令
                send_command(conn, cmd, dev['vendor'])

                # 等待命令完成,获取输出
                output = recv_command_output(conn, dev['vendor'], timeout=30)
                last_output = output

                # 如果有delay,休眠
                if delay > 0:
                    time.sleep(delay)
                    print(f"  [DELAY] {delay}s after '{cmd}'")

                # 保存到设备专属日志
                save_to_file(dev['ip'], cmd, output)

    except Exception as e:
        error_msg = f"[ERROR] {dev['ip']} - {str(e)}"
        print(error_msg)
        save_error_log(dev['ip'], error_msg)
        continue  # 失败一台,继续下一台

parse_command()函数是关键:

def parse_command(line):
    cmd = line
    delay = 0
    skip_cond = None
    if ';' in line:
        cmd_part, rest = line.split(';', 1)
        cmd = cmd_part.strip()
        for param in rest.split(';'):
            param = param.strip()
            if param.startswith('delay='):
                delay = int(param[6:])
            elif param.startswith('skip_if_contains='):
                skip_cond = param[17:]
    return cmd, delay, skip_cond

它把save;delay=25拆成cmd='save', delay=25, skip_cond=None;把show run;skip_if_contains=Invalid拆成cmd='show run', delay=0, skip_cond='Invalid'

Step 3:结果保存:每个设备一份独立日志

脚本不会把所有输出塞进一个大文件。它为每台设备创建独立日志:
- 文件名:{IP}_result.txt,如192.168.1.1_result.txt
- 内容格式:
```text
=== 192.168.1.1 (huawei) - 2024-06-15 22:30:45 ===

— Command: display version —
Huawei Versatile Routing Platform Software
VRP (R) software, Version 8.180 (S5735 V200R022C00SPC500)
Copyright (C) 2012-2023 Huawei Technologies Co., Ltd.

— Command: display device manuinfo —
S5735-L1-24P4X:
Manufactured at: 2023-03-15
Serial Number: 210235A1TQCB12345678

=== END ===
```

这种格式,让排查问题变得极其简单:运维同事直接grep "Serial Number" *.txt就能导出所有设备序列号;find . -name "*result.txt" -exec grep -l "CPU.*95%" {} \;就能定位高负载设备。

4.4 输出结果:不只是文本,更是可审计的操作证据

脚本运行结束后,当前目录会多出N个*_result.txt文件,以及一个summary.log

summary.log内容示例:

[2024-06-15 22:30:45] STARTED
[2024-06-15 22:30:45] Loaded 5 devices from hosts.txt
[2024-06-15 22:30:45] Loaded 8 commands from commands.txt

[2024-06-15 22:30:46] SUCCESS 192.168.1.1 (huawei) - 8 commands executed
[2024-06-15 22:30:52] SUCCESS 192.168.10.1 (h3c) - 8 commands executed
[2024-06-15 22:30:58] SUCCESS 192.168.10.2 (h3c) - 8 commands executed
[2024-06-15 22:31:05] SUCCESS 192.168.20.1 (telnet) - 8 commands executed
[2024-06-15 22:31:12] FAILED 10.10.100.1 (cisco) - Authentication failed

[2024-06-15 22:31:12] FINISHED - 4/5 devices succeeded
[2024-06-15 22:31:12] Results saved to: 192.168.1.1_result.txt, ..., 192.168.20.1_result.txt

这份摘要日志,是给变更管理流程看的。它记录了操作时间、设备数量、成功率、失败原因,满足ITIL事件管理的基本审计要求。你可以把它直接粘贴进Jira工单,或者作为邮件正文发送给值班经理。

5. 常见问题与排查技巧实录:那些文档里不会写的“血泪经验”

再完美的工具,在真实网络里也会遇到意外。下面这些,全是我在客户现场、深夜割接、紧急排障中,亲手验证过的解决方案。它们不在官方文档里,但比文档管用十倍。

5.1 连接失败类问题

问题1:SSHException: Error reading SSH protocol banner
现象: 脚本卡在Connecting to 192.168.1.1...,几秒后报错。
原因: 目标交换机SSH服务禁用了banner(ssh server banner none),而paramiko默认严格校验banner。
解决:1.py中找到connect_ssh()函数,修改paramiko.SSHClient()初始化部分:

# 原始代码(会校验banner)
client = paramiko.SSHClient()

# 修改为(忽略banner)
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 新增:禁用banner校验
transport = client.get_transport()
if transport is None:
    client.connect(
        hostname=dev['ip'],
        port=dev['port'],
        username=dev['username'],
        password=dev['password'],
        look_for_keys=False,
        allow_agent=False,
        banner_timeout=10  # 关键!增加banner等待超时
    )
else:
    # 已连接,跳过
    pass

banner_timeout=10告诉paramiko,最多等10秒banner,超时就继续握手。这是最安全的修复,无需改源码。

问题2:pexpect.TIMEOUT on Telnet connection
现象: Telnet连接时,脚本报pexpect.TIMEOUT,但手动telnet能连上。
原因: pexpect.spawn默认的timeout太短(30秒),而某些老旧H3C设备在登录前会输出大量%Jan 1 00:00:00:000 2023 H3C SHELL/5/SHELL_LOGIN:日志,占满缓冲区,导致spawn.expect(['Username:', 'login as:'])迟迟不触发。
解决:connect_telnet()函数中,增大spawntimeout并清空初始缓冲:

child = pexpect.spawn(f'telnet {dev["ip"]} {dev["port"]}', timeout=60)
child.logfile = open('/tmp/telnet_debug.log', 'wb')  # 开启调试日志
child.expect(['Username:', 'login as:', pexpect.TIMEOUT], timeout=60)
if child.after == pexpect.TIMEOUT:
    # 清空缓冲,重试
    child.send('\r\n')
    child.expect(['Username:', 'login as:'], timeout=30)

5.2 命令执行类问题

问题3:save命令后脚本卡住,不继续执行下一条
现象: 日志停在--- Command: save ---,无后续输出。
原因: 华为/H3C设备在save后,会输出The current configuration will be written to the device. Are you sure? [Y/N]:,脚本没识别这个提示,一直在等<HUAWEI><H3C>提示符。
解决:send_command()后,增加对确认提示的主动响应:

def send_command(conn, cmd, vendor):
    if vendor in ['huawei', 'h3c']:
        # 对于save/write命令,主动发送'y'
        if cmd.strip().lower() in ['save', 'write', 'write memory']:
            conn.send(cmd + '\n')
            # 立即等待确认提示
            time.sleep(1)
            conn.send('y\n')
            return
    conn.send(cmd + '\n')

问题4:display interface brief输出被截断,只看到前10行
现象: 结果文件里,接口列表只有GigabitEthernet0/0/1GigabitEthernet0/0/10,后面没了。
原因: 设备分页未关闭,display interface brief返回--More--后停止输出。
解决: 脚本在登录成功后,会自动发送screen-length 0 temporary(华为)或screen-length disable(H3C)或terminal length 0(Cisco)。但如果设备不支持这些命令(如极老版本),脚本会捕获Unrecognized command错误,然后尝试发送空格()来跳过分页。你可以在commands.txt第一行加上:

screen-length 0 temporary;skip_if_contains=Unrecognized

这样,脚本会先尝试关闭分页,失败则跳过,继续执行后续命令。

5.3 环境与权限类问题

问题5:Windows上运行报ModuleNotFoundError: No module named 'pexpect'
现象: pip install -r requirements.txt成功,但python 1.py仍报错。
原因: Windows上存在多个Python环境(如Anaconda、Python.org官方版、Microsoft Store版),pippython可能指向不同环境。
解决: 统一使用绝对路径:

# 查看python位置
where python

# 查看pip位置
where pip

# 如果不一致,用python -m pip安装
C:\Python39\python.exe -m pip install -r requirements.txt

问题6:Linux上运行报pexpect.exceptions.ExceptionPexpect: The command was not found
现象: Telnet连接时报错,提示telnet命令找不到。
原因: CentOS/RHEL最小化安装默认不带telnet客户端。
解决: 安装telnet:

# CentOS/RHEL
sudo yum install -y telnet

# Ubuntu/Debian
sudo apt-get install -y telnet

或者,更推荐的方式:在1.py中,当检测到pexpect不可用时,自动切换到Python标准库telnetlib(已内置,无需安装)。

5.4 高级技巧:让脚本更“懂你”

技巧1:动态生成hosts.txt
你不需要手动维护IP列表。写一个gen_hosts.py

# gen_hosts.py
import subprocess
# 从CMDB API拉取在线交换机
result = subprocess.run(['curl', '-s', 'https://cmdb/api/devices?type=switch&status=online'], 
                       capture_output=True, text=True)
# 解析JSON,生成hosts.txt
with open('hosts.txt', 'w') as f:
    for dev in result.stdout.splitlines():
        f.write(f"{dev['ip']}|{dev['user']}|{dev['pass']}|{dev['vendor']}\n")

每天定时任务跑一次,hosts.txt永远最新。

技巧2:结果自动分析
1.py末尾加一段:

# 分析所有_result.txt,找出CPU>80%的设备
high_cpu_devices = []
for file in glob.glob('*_result.txt'):
    with open(file, 'r') as f:
        content = f.read()
        if 'CPU Utilization' in content and '80%' in content:
            high_cpu_devices.append(file.replace('_result.txt', ''))
if high_cpu_devices:
    print(f"\n[ALERT] High CPU detected on: {', '.join(high_cpu_devices)}")

运行完脚本,立刻知道哪些设备需要重点关注。

技巧3:对接企业微信机器人
summary.log内容,通过Webhook发到企微群:

import requests
webhook_url = "https://qyapi.weixin.qq.com/..."
requests.post(webhook_url, json={
    "msgtype": "text",
    "text": {"content": f"批量巡检完成:{success_count}/{total_count} 成功"}
})

值班手机立刻收到通知,无需守着终端。

6. 后续演进与个人体会:它为什么永远不会变成“另一个Ansible”

这个工具包,从2021年第一个1.py雏形,到现在稳定运行于17个客户网络,它的进化路径非常清晰:不做加法,只做减法;不追新功能,只填旧坑。 我们拒绝加入以下特性:

  • ❌ 不支持HTTP API:交换机RESTful API碎片化严重,华为iMaster NCE、H3C IMC、锐捷RIIL各自为政,维护成本远超收益。
  • ❌ 不集成配置备份:display current-configuration已足够,真正的配置备份应由专业网管系统(如SolarWinds、NetBrain)承担。
  • ❌ 不提供Web界面:命令行就是最好的UI——它可脚本化、可审计、可管道传递(python 1.py | grep "Serial")。

它存在的唯一理由,是成为你键盘和交换机之间,那根最可靠、最透明、最不抢戏的“数据线”。当你深夜需要快速确认20台设备的固件版本,当你割接前要批量shutdown冗余端口,当你被要求出具一份“所有接入交换机CPU使用率截图”——它不会让你等,不会让你配,不会让你猜。它就静静地躺在那里,python 1.py,然后给你20份干净的结果。

我个人在实际使用中发现,最宝贵的不是它节省了多少时间,而是它消除了那种“不确定感”。以前手工操作,做完总要怀疑:“刚才那台是不是漏敲了y?”“display interface的输出,我有没有看漏第15行?”现在,打开192.168.10.5_result.txt,Ctrl+F搜GigabitEthernet1/0/15,答案就在那里,白纸黑字,无可辩驳。这种确定性,是任何自动化工具最底层的价值——它不创造新知识,但它把人的注意力,从“我有没有做错”解放出来,投向真正重要的问题:“为什么这台设备的端口状态总是flapping?”、“这个CPU峰值,是流量突发还是硬件故障?”

工具终会迭代,但这个理念不会变:好的运维自动化,不是让你远离设备,而是让你更专注地理解设备。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:一套轻量级命令行工具,用Python实现对多台交换机的批量远程操作。核心脚本1.py读取hosts.txt里的IP列表(支持指定端口、用户名、密码),再通过SSH或Telnet连接每台设备,依次执行commands.txt中定义的CLI指令,比如display version、save、interface shutdown等,并将每台设备的返回结果分别保存为独立文本文件。底层基于paramiko(SSH)和pexpect(Telnet)兼容主流厂商设备,包括华为、H3C、锐捷、Cisco等。不需要图形界面,Windows/macOS/Linux均可运行,Python 3.6+环境直接安装requirements.txt依赖即可使用。适合网络工程师做日常巡检、配置同步、批量重启、升级前检查等重复性任务,减少手动逐台登录带来的耗时和输错风险。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值