资讯动态

Python+Netmiko实现H3C交换机自动巡检与状态解析

发布时间:2026/10/17 13:48:15 来源:尧图企业网站定制
简介在IT运维中H3C交换机巡检常受制于手动登录、逐台执行命令的低效方式。针对这一场景Python自动化巡检的实现详解从netmiko连接配置讲起逐步演示通过SSH批量登录交换机执行display ip routing-table、dis version、display fan、display cpu-usage、display memory等命令并将返回结果按设备写入文本。随后使用xlwt把文本转为Excel表格并借助smtplib发送邮件或钉钉机器人推送报告最终配合Linux crond实现定期无人值守巡检。全部内容浓缩为1个PDF文件大小33KB篇幅精简但流程完整、代码可复用。已有4413人学习/下载适合有Python基础且需维护多台H3C设备的运维人员可有效减少重复操作、降低漏检风险、提升排障效率。1. Python自动巡检H3C交换机从手工登录到脚本批量拉回状态早几年我还在某公司做网络运维时每天早上最枯燥的事就是登录几十台H3C交换机逐台敲display cpu、display memory、display interface brief再把屏幕上滚动的字符复制进一个带日期的文本文件里。遇到设备多了或者登录卡顿一上午就这么没了还总担心漏掉哪一台。后来我用 Python 写了套自动巡检脚本把登录、取数、初判都交给程序完成我只需要在上班后打开一份汇总报告。这篇笔记就把这个实现过程拆开讲怎么选协议、怎么封装登录、怎么解析回显、有哪些坑必须提前躲开。它不只适合网络工程师也适合正在做运维自动化的开发同学因为 H3C 设备足够典型这套思路换到其他厂商设备同样成立。2. 巡检H3C交换机前先定三件事协议、资产表、运行环境动手写脚本之前如果直接跳到连接代码大概率会在批量执行时翻车。我先定清楚三件事用 SSH 还是 SNMP 拿数据、设备清单怎么维护、脚本跑在什么环境里。这三件事没想明白后面每调一次都要改结构。2.1 为什么我最终用 SSH Netmiko 而不是 SNMP一提到网络设备采集很多人第一反应是 SNMP 轮询。SNMP 的好处是标准、轻量、适合监控系统持续拉取但放到我这个巡检场景里有个明显短板我只能拿到 OID 定义好的数据比如接口流量、CPU 均值可我想顺带看设备版本、告警日志、端口物理状态这些内容很多不在标准 MIB 里或者需要在设备上做额外映射。另外一个实际问题当时设备上的 SNMP 配置并不统一有人开了 v2c有人只开 v3还有的 community 串连自己都不清楚。操作用户名密码登录就简单得多只要运维账号没被禁用所有状态都能通过命令行拿到。所以我选了 SSH 通道而且没有直接用 Paramiko而是在它之上用了 Netmiko。Paramiko 只提供 SSH 会话能力分页处理、提示符等待、命令回显清理都要自己写Netmiko 把这些通用逻辑封装好了还针对不同厂商设备定义了不同行为H3C 设备在它的设备类型表里有专门条目直接指定device_typeh3c就能用。有人会问H3C 和 HPE Comware 是不是同一个设备类型早期版本里 netmiko 对两者处理有差异实际使用中我更喜欢单独写h3c避免提示符匹配规则不对导致命令发不出去。安装依赖也简单一个 netmiko 就能覆盖 SSH 连接和命令交互。我在干净的虚拟环境里执行pip install netmiko安装完成后可以用下面这行快速验证库是否就位不输出错误就说明环境没问题。python -c import netmiko; print(netmiko.__version__)需要注意的是我这边没有额外安装 paramiko因为 netmiko 会自动把它带进来。如果你所在的办公网对外网受限离线安装时记得把 netmiko 的依赖一起下载常见依赖包括 paramiko、scp、textfsm、ntc-templates 等具体列表以你使用的 netmiko 版本为准。2.2 把设备资产表整理成 CSV少填一个字段后面就多花一小时H3C 设备的登录参数虽然是固定的几项但批量脚本运行的前提是有一份可靠的设备清单。我见过有人在 Python 脚本里硬编码一个列表devices [{ip: 192.168.1.1, ...}]这种做法在小规模验证时没问题等设备增加到几十台修改脚本就要重新部署还容易把账号密码提交到版本库。我的习惯是把设备信息抽到一个独立 CSV 文件里脚本只负责读它。资产表至少应该包含这几列ip、username、password、port、device_type。如果有人用不同管理账号访问不同设备也可以加一列account_group但初期不建议把模型设计得太复杂五列够用。下面是我当时使用的devices.csv示例ip,username,password,port,device_type 192.168.10.11,ops_admin,Str0ngPss,22,h3c 192.168.10.12,ops_admin,Str0ngPss,22,h3c 192.168.10.13,netops,AnotherPass123,22,h3c读取 CSV 时我建议直接用 Python 标准库csv不要为了这一步引入 pandas。网络设备数量级通常在几十到几百台标准库足够还能减少环境依赖。下面这段代码会把每一行转成字典并顺便过滤掉空行import csv def load_devices(path): devices [] with open(path, newline, encodingutf-8) as f: reader csv.DictReader(f) for row in reader: if row.get(ip): devices.append(row) return devices if __name__ __main__: for d in load_devices(devices.csv): print(d[ip], d[device_type])这段代码的逻辑很简单用DictReader读取表头自动变成字典的键循环里只追加非空设备行。关于密码字段有一点要注意如果密码里包含逗号或引号CSV 必须用双引号包起来否则行会被拆错。我在项目里曾经因为某个密码里带了#没出问题但换成一个包含逗号的密码后脚本怎么都对不上设备排查半天才发现是 CSV 解析错了。2.3 运行环境与依赖Python3.6一个 Netmiko 库够用运行环境上我在两台机器都跑过这套脚本一台是 Windows 10 办公电脑一台是 CentOS 7 服务器。Python 版本至少需要 3.6因为 netmiko 使用了一些新语法3.6 以下容易出现兼容问题。我现在习惯用 3.8 及以上主要是正则和字符串处理更顺手不过没必要为了这个脚本单独升级生产环境。另外确保网络能达到目标设备的 TCP 22 端口。很多办公室规定了运维网段白名单脚本执行机如果不是从运维网段发出连接第一步就会卡在超时上。我的做法是在脚本里先对 IP 做一次socket连通性预检快速跳过不可达设备避免后面每台设备都等连接超时。下面这段探测代码可以放在批量循环前import socket def is_reachable(ip, port22, timeout3): try: with socket.create_connection((ip, port), timeouttimeout): return True except OSError: return False这个预检不是万能的但它能把网络层的“连不上”和账号层的“认证失败”区分开日志里报错会清晰很多。我一般会给它 3 秒超时因为一台设备不可达时等待 3 秒比等 TCP 默认超时快得多。整个脚本只依赖 netmiko 和标准库不用弄一堆工具链也方便其他人接手。3. 用Netmiko连接H3C交换机并批量执行命令最小可用代码与参数调整到了这一步基础条件已经准备好接下来就是真正写连接逻辑。很多人一开始就试图写一个包含所有功能的“大函数”结果越来越难调。我建议先写一个单设备连接的骨架确认能跑通再扩展成批量版本。3.1 单台连接的骨架三步走连接、关分页、执行命令连接 H3C 交换机的最小代码如下我把它放在connect_one.py里from netmiko import ConnectHandler device { device_type: h3c, ip: 192.168.10.11, username: ops_admin, password: Str0ngPss, port: 22, timeout: 15, global_delay_factor: 2, } dev_conn ConnectHandler(**device) # 关闭临时分页避免命令回显被截断 dev_conn.send_command(screen-length 0 temporary) # 关闭终端日志推送防止告警信息插在命令回显里 dev_conn.send_command(undo terminal monitor) output dev_conn.send_command(display cpu) print(output) dev_conn.disconnect()这里ConnectHandler接收的是连接参数字典**device将字典展开成关键字参数。device_type决定了 netmiko 用哪套提示符匹配规则和命令发送方式H3C 设备用h3c。timeout是连接阶段的等待时间我设置 15 秒是因为有些老设备 SSH 服务启动慢。global_delay_factor是 netmiko 里的通用延时放大倍数所有读写操作的默认等待时间都会乘以这个值H3C 设备 CPU 性能弱设为 2 能明显降低空回显概率。连接成功后send_command会发送命令并等待提示符重新出现。注意我特意先发了screen-length 0 temporary和undo terminal monitor这两条不是可有可无。前者把当前会话的分页长度设为 0让命令回显一次全部输出后者关闭终端监视器防止端口 up/down、日志等异步消息混入回显里。很多人只发第一条忘记关日志后面解析时就会看到奇怪的行。3.2 H3C登录慢和回显分页两个影响稳定性的参数H3C 设备尤其是老款交换机SSH 登录初始化和命令回显速度明显比新设备慢。如果使用 netmiko 默认参数很容易出现Read timeout或者日志中报“找不到提示符”。除了前面说的global_delay_factor还有fast_cli这个参数值得说明一下。fast_cliTrue会让 netmiko 关闭verbose输出并尝试减少多余等待但不要盲目开启因为它在慢速 H3C 设备上反而会降低成功率。我的建议是默认先不设置fast_cli遇到明显卡顿的设备再单独调。另一个问题是命令回显分页。即使发送了screen-length 0 temporary在某些 Comware 版本上这条命令本身可能不在当前管理员权限下可用。如果发送时报权限错误不必中断可以继续发screen-length disable试试或者把display命令改成带| no-more的形式。实际上 H3C 的display命令支持管道符例如display cpu | no-more这也是跳过临时配置的一个办法。我在脚本里的处理是优先用临时配置方案因为不同版本对| no-more的支持并不一致。还有一个容易被忽略的细节send_command默认会启用自动分页处理其实 netmiko 对 h3c 设备常用配置里会发送分页禁用命令但为了兼容性和可控性我自己显式发一次更安心。而且显式发送还有一个好处后续同一会话再执行长输出时不会有分页残留问题。3.3 从单台到批量循环里如何处理会话与异常连接一台设备成功之后批量就是循环加异常处理的活。批量脚本要保证一台设备出错不能拖垮整体所以try/except是必须的。下面是我常用的批量执行模板它会把每台设备每个命令的输出分别存到以 IP 命名的文本文件里from netmiko import ConnectHandler from concurrent.futures import ThreadPoolExecutor, as_completed import csv import os def inspect_single(device): try: conn ConnectHandler(**device) conn.send_command(screen-length 0 temporary) conn.send_command(undo terminal monitor) result {} for cmd in [display cpu, display memory, display interface brief]: result[cmd] conn.send_command(cmd) conn.disconnect() return device[ip], result, None except Exception as err: return device[ip], None, str(err) def load_devices(path): devices [] with open(path, newline, encodingutf-8) as f: for row in csv.DictReader(f): if row.get(ip): row[timeout] 15 row[global_delay_factor] 2 devices.append(row) return devices if __name__ __main__: devices load_devices(devices.csv) success_count 0 with ThreadPoolExecutor(max_workers10) as pool: futures {pool.submit(inspect_single, d): d for d in devices} for fut in as_completed(futures): ip, result, err fut.result() if err: print(f{ip} 巡检失败: {err}) else: success_count 1 os.makedirs(results, exist_okTrue) with open(fresults/{ip}.txt, w, encodingutf-8) as f: for cmd, output in result.items(): f.write(f### {cmd}\n) f.write(output) f.write(\n) print(f完成成功 {success_count}/{len(devices)})这里用了ThreadPoolExecutor做并发因为 SSH 连接是 IO 密集型的串行跑 30 台设备可能要半小时并发 10 线程能把时间压缩到几分钟。max_workers10是个比较保守的值既不会因为线程太多压垮设备管理 CPU也能明显提速。每个线程里inspect_single独立处理一台设备内部用异常捕获隔离错误即使某一台认证失败进程也会继续执行后续设备。运行结束后results目录下会出现以 IP 命名的文件每个文件里按命令分段保存了原始回显。需要提醒的是ConnectHandler创建会话后用disconnect()关闭是必要的。如果某个异常发生在disconnect()之前连接对象会在垃圾回收时释放但自动释放时机不可控长时间运行会堆积大量半开连接。更好的做法是用with上下文管理器不过 netmiko 的历史版本对上下文支持有差异所以我这里选择了显式断开的写法至少在异常路径上已经打印了错误不会静默泄漏。4. 把display回显解析成结构化数据正则提取CPU、内存与端口状态巡检不只为了把原始回显存档更重要的是让程序能判断设备是否健康。如果每次都要人工打开文本文件看 CPU自动化的意义就少了一半。所以下一步是把命令回显切成能入库、能对比的字段。4.1 巡检项对应的H3C命令清单H3C Comware 系统里有几个经典巡检命令不同版本回显格式略有差异但关键字段高度一致。我把常用的整理成一张表脚本里按需增减。巡检目标display 命令关键字段CPU 占用率display cpuCPU usage、slot 编号内存占用率display memoryMemory Usage、Total/Free接口状态display interface briefLink 状态为 UP/DOWN、速率、Duplex设备温度display environment当前温度、告警阈值系统版本与运行时间display versionSoftware Version、uptime设备硬件状态display deviceStatus 为 Normal/Fault建议不要一开始就采集所有命令先选 CPU、内存、接口状态这三项。解析逻辑稳定后再加温度和设备状态因为温度回显在虚拟化平台和实体机上差异较大设备型号不同字段名也不同过早收集会让第一版脚本卡在解析兼容性上。4.2 用正则提取CPU和内存的数值H3C 的display cpu回显通常长这样Slot 1 CPU 0: CPU 0 (Slot 1) current state : Normal CPU usage: 4.5%不同型号可能在前面加上DRAM或runtime标识但真正要抓的是4.5%这个数字。用正则提取时要注意两点数字是浮点数百分号后可能有空格或换行另外设备只有一块主控时回显里也可能没有 Slot 信息正则要兼容两种情况。我写的提取函数如下import re def parse_cpu(text): # 匹配类似 CPU usage: 4.5% 的片段 m re.search(rCPU\susage[^0-9]*([0-9]{1,2})(?:\.[0-9])?, text, re.IGNORECASE) if m: return float(m.group(1)) # 兼容 CPU 0 (Slot 1) current state : Normal 这种无百分比的回显 m2 re.search(rcurrent state\s*:\s*(\w), text, re.IGNORECASE) if m2: state m2.group(1).lower() if state normal: return 0 return None这个函数里的第一个正则有瑕疵([0-9]{1,2})(?:\.[0-9])?只能匹配一位小数而且没有把小数点前整数和后面小数组合成一个合法浮点数。实际跑下来发现H3C 有的版本显示4.5%有的显示4%所以我改成捕获完整数字更稳妥def parse_cpu(text): m re.search(rCPU\susage\s*[:]?\s*([0-9]{1,3}(?:\.[0-9]{1,2})?)\%, text, re.IGNORECASE) if m: return float(m.group(1)) return None这里把百分号也放进匹配范围避免误匹配到描述文本里的其他数字。内存解析思路相同display memory回显通常有类似Memory Used: 8947840 KB、Memory Free: 12977456 KB或者直接给百分比。我会同时抓已用和空闲计算使用率使用率 已用 / (已用 空闲)。直接抓百分比字段省事但有些版本不直接给百分比所以用两个字段自己算更通用。def parse_memory(text): used re.search(rMemory Used[^0-9]*([0-9]), text, re.IGNORECASE) free re.search(rMemory Free[^0-9]*([0-9]), text, re.IGNORECASE) if used and free: used_kb float(used.group(1)) free_kb float(free.group(1)) return used_kb / (used_kb free_kb) * 100 return None正则解析这类文本最大的风险不是写错模式而是设备回显里混入了转义字符。比如有些设备开启了彩色终端回显里会有形如\x1b[0m的 ANSI 转义码肉眼看不出来但正则匹配时会被打断。这里推荐在进入解析函数前先用一个清理函数把 ANSI 控制字符剥掉def strip_ansi(text): ansi_re re.compile(r\x1b\[[0-9;]*[mK]) return ansi_re.sub(, text)调用时先text strip_ansi(text)再交到解析函数能省掉很多玄学问题。4.3 解析失败别硬扛保存原始回显作为兜底正则再怎么兼容也挡不住设备版本升级后回显格式大变。我第一版脚本上线后就遇到过某型号交换机升级固件后display memory字段从英文变成了中文正则全部匹配不到。这时候如果脚本直接返回None后续做阈值判断就会把这个设备当作“数据缺失”处理既不准确也不直观。我养成的习惯是每一台设备的原始回显一定要落盘保存解析成功与否都保留。前面批量脚本已经写了results/{ip}.txt解析完成后我再生成一个parsed_{ip}.json里面是字段名和解析结果。如果某个字段解析失败在 JSON 里写成null同时写一条日志到warnings.log。这样出了问题我既能拿到原始数据二次分析也能在报告里看到哪个设备哪一项数据缺失。import json def parse_and_dump(ip, raw_outputs, parsed_pathparsed_results): parsed {} os.makedirs(parsed_path, exist_okTrue) for cmd, output in raw_outputs.items(): cleaned strip_ansi(output) if cpu in cmd: parsed[cpu] parse_cpu(cleaned) elif memory in cmd: parsed[memory] parse_memory(cleaned) else: parsed.setdefault(cmd, None) with open(f{parsed_path}/{ip}.json, w, encodingutf-8) as f: json.dump(parsed, f, ensure_asciiFalse, indent2)这个兜底思路比精确解析更重要。哪怕解析代码写得很完美也会遇到设备响应超时、接口状态字段拼接等边缘情况。保留原始回显就等于给每条数据买了后悔药。5. 自动巡检H3C交换机的避坑指南5个让我白天想睡觉的坑脚本能跑不等于稳定更不等于可以完全放开不管。下面这几个坑我都是在真实环境里踩过以后才慢慢摸索出解决办法的每一条都值得提前处理。5.1 现象回显只有第一页后面的数据不见了原因H3C 设备默认分页显示长输出执行display interface brief这类命令时输出超过一屏就暂停等待用户按键。netmiko 即使一直在读也只会拿到当前这一屏内容而且因为提示符没出现send_command会一直等到超时。解决连接后先发送screen-length 0 temporary把当前会话的分页禁用掉。如果这条命令发送报错再尝试screen-length disable或者用display xxx | no-more的形式执行命令。我建议两条链路都写在代码里第一条失败就自动切换第二条保证高版本 Comware 也一样稳定。5.2 现象批量脚本卡死在某台设备上后面所有设备都不跑了原因连接时代理错误或认证失败会让ConnectHandler立即抛异常但如果设备 IP 不可达TCP 连接会一直挂在阻塞状态或者发送命令后设备回显异常netmiko 等提示符等不到默认超时时间又很长。解决必须给ConnectHandler传入合理的timeout同时在每台设备的外层用try/except包裹。前面批量模板里我已经把timeout设为 15global_delay_factor设为 2这两项需要根据现场设备响应速度调整。如果日志显示某台设备经常超时可以单独给这台设备加一个超时参数而不是全局放大延时避免拖慢整个批次。5.3 现象CPU使用率解析出 None正则明明看着没问题原因设备回显里带有 ANSI 彩色控制符或者行尾的\r符号正则在CPU usage:后紧跟数字时被不可见字符隔开了。我遇到过一种情况屏幕上看是CPU usage: 4.5%但十六进制里有\x1b[0m插在冒号和数字之间。解决进入解析函数前先做strip_ansi清理并且把回显里的\r\n统一成\n。我在多个型号上对比过清理后解析成功率从 80% 提升到了接近 100%。另外建议在解析函数里加一层防御如果正则匹配失败不要直接返回None而是把原始文本片段的前 200 字符记录到日志里方便回查。5.4 现象连接时控制台日志刷屏命令回显全乱了原因H3C 设备默认会向登录会话推送端口状态、链路振荡等日志信息。如果有人在巡检同时动了网线日志就会插进display cpu的回显中间导致send_command拿到的输出包含大量无关文本解析自然出错。解决连接后立即发送undo terminal monitor关闭终端日志推送。注意这条命令只影响当前会话不会影响设备日志功能。如果某些设备提示命令不可用可能是低版本 Comware 或用户权限不足可以在连接参数里允许失败继续不要让它阻断后续巡检。5.5 现象某个命令偶尔超时但人工执行就正常原因设备 CPU 瞬时繁忙或者当时正好有业务流量触发大量转发任务导致 SSH 会话响应延迟。还有一种情况是 netmiko 的send_command默认等待提示符的超时时间太短设备还没输出完命令会话就被判定超时。解决给send_command单独传timeout或read_timeout参数比如这个长命令单独用更大的等待时间。再配合一次自动重试第一次超时后等几秒重发一次。我在脚本里对display environment这类慢命令做了重试逻辑捕获异常后 sleep 3 秒再执行基本能覆盖偶发延迟。6. 让巡检脚本每天早上自动跑定时调度与异常通知的小技巧脚本写完、解析稳定之后还差最后一步让它自己跑起来。我在生产环境用 CentOS 服务器做调度crontab 配置很简单30 7 * * 1-6 cd /opt/h3c_inspection python3 run_inspection.py logs/cron_$(date \%Y\%m\%d).log 21每周一到周六早上 7:30 跑一次周日留空因为那天基本不看设备状态。脚本运行完会生成summary.txt我只需要打开看一眼成功设备和失败设备数量。如果是 Windows 办公电脑就用任务计划程序设置触发器命令填python.exe参数填脚本完整路径。为了能快速发现“少了一台设备”我习惯在脚本最后加一段统计逻辑对比 CSV 里的设备总数和解析 JSON 文件数量。数量对不上就说明有设备连接失败或解析中断这时再去看日志。这个简单的数量比对比刷几千行日志有效得多。如果要更进一步可以把失败记录写进一个failed.txt调度任务里再用邮件或企业微信通知模板推送但一开始没必要为了通知功能引入额外依赖。我在这套脚本上最大的教训是自动化跑起来之后一定要保留人工复核的窗口。我有一次更新了设备登录密码脚本每天定时失败我却因为只看汇总数字以为“数量对不上只是偶发”直到第三天排查才发现账号过期。后来我给自己定了个习惯无论自动化多稳定每天早上至少抽查一台设备的原始回显确认命令输出内容和人眼看到的一致。这个习惯帮我提前发现过至少两次设备回显格式变更。希望这篇笔记能让你在搭自己的 H3C 自动巡检时少走弯路跑通之后记得把原始回显留好它会是排查时最靠得住的后路。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑