简介:一套基于NETCONF协议构建的网络设备配置管理系统源码包,面向网络运维开发人员与华为CE系列交换机使用者,解决手工配置效率低、易出错等问题。系统采用YANG模型描述配置数据结构,针对华为CE12800和CE6800交换机设计自动配置脚本下发与收集逻辑,并提供图形化客户端界面与网络拓扑监控功能,便于直观管理设备运行状态。资源共376个文件,压缩包约2.48MB,主要包含128个Python后端脚本、103个Vue前端组件、84个JavaScript逻辑文件,以及SCSS样式、PNG图标、Markdown文档等,前后端分层清晰,环境配置与说明文档一应俱全,适合作为项目模板或二次开发基础。已有169人学习下载。压缩包内附赠说明文档及完整项目文件,涵盖核心功能模块与界面素材,适合想深入理解NETCONF/YANG实操、或在此基础上扩展维护功能的开发者参考使用。
1. 基于NETCONF协议的网络设备配置管理系统:先把手工运维的账算清楚
手头有几十台华为CE12800和CE6800,某天晚上临时接到需求:全网NTP服务器地址要改,管理口ACL要收敛。老办法是逐台登录敲命令,运气好一台三分钟,运气差碰到分页、回显编码、命令超长,一个晚上搭进去还容易敲错。这个标题描述的方案,本质就是围绕NETCONF协议建一套配置管理系统:设备侧开启基于SSH的NETCONF会话,客户端侧用YANG模型精确描述“读什么、改什么”,配置脚本自动下发、自动收集,再把结果落到图形化界面上做任务跟踪和拓扑监控。适合正在做数据中心网络运维、想摆脱CLI脚本脆断、又不想被SNMP只读能力卡住的人。
2. NETCONF与YANG模型:为什么说这条路线比SNMP和CLI脚本更适合批量配置
先说结论:如果你的设备全是华为CE系列,NETCONF + YANG是做批量配置管理性价比最高的路线。CLI脚本的本质是模拟人敲键盘,遇到分页、回显、缩进、版本差异就容易脆断;SNMP的MIB偏监控设计,读指标很顺,但写配置的能力弱且语义模糊。NETCONF把配置管理抽象成标准操作,数据模型由YANG统一描述,设备只会按模型应答,不再依赖“命令字符串猜谜”。
| 对比项 | CLI脚本 | SNMP | NETCONF + YANG |
|---|---|---|---|
| 传输通道 | SSH/Telnet | UDP 161 | SSH(默认830端口) |
| 数据表达 | 命令字符串 | OID + ASN.1 | XML结构化报文 |
| 读配置 | 解析回显文本 | get/getnext | get-config |
| 写配置 | 模拟键盘输入 | SET(受限) | edit-config / copy-config |
| 数据模型 | 无 | MIB | YANG模块 |
| 事务与回滚 | 无 | 无 | error-option、candidate库(看设备) |
2.1 NETCONF协议栈:SSH承载、XML报文与四种核心操作
NETCONF是IETF的RFC 6241定义的配置管理协议,默认承载在SSH上,独立端口830。客户端和设备建立的是长连接会话,不是“敲一条命令返回一条结果”,而是类似数据库连接:建立会话、协商能力、执行操作、关闭会话。报文全部是XML,这意味着你不用写正则去拆回显文本,直接用XML解析器拿节点值。
协议层把操作收敛成有限的几个:get、get-config、edit-config、copy-config,外加lock、unlock、close-session。批量配置管理系统里,最常用的组合是get-config读当前配置、edit-config改单个节点或一段配置、copy-config做整机配置覆盖或回滚。edit-config还有个重要参数error-option,可以指定出错时是停止、继续还是回滚,后面批量下发时会专门用到。
2.2 YANG模型:把设备配置变成一棵可读、可写、可校验的树
YANG(RFC 6020 / 7950)解决的问题是:设备配置长什么样、哪些参数合法、节点之间什么关系。NETCONF传输的是XML实例,而XML实例必须符合某个YANG模型描述的结构,否则设备不认。可以理解成YANG是表结构定义,XML报文是按表填的数据记录。
华为CE系列把接口、VLAN、路由、LLDP、AAA等能力都建模成独立的YANG模块。比如要读接口状态,客户端发一个XML过滤器,里面引用接口模块的命名空间,只取需要的节点。这个“命名空间”是个关键细节,它通常带版本信息,设备在NETCONF会话建立时会主动通告自己支持的schema,客户端不能拿一份网上随便下载的YANG文件硬套所有设备。
2.3 华为CE12800与CE6800的NETCONF能力对比与选型判断
CE12800是框式核心/汇聚设备,CE6800是TOR接入交换机,两者操作系统同属CloudEngine系列,NETCONF基础能力一致,但YANG模块覆盖范围有明显差异。CE12800因为涉及集群、跨设备链路聚合、虚拟化等特性,YANG模块更多、树更深;CE6800相对精简。接口命名风格也不一样,CE6800常见的是10GE1/0/1这种固定口格式,CE12800靠接口板槽位区分,解析时需要注意。
选型判断上,我一般看三点。第一,如果管理对象全是一两家厂商的设备,直接对齐厂商YANG模型最省事;第二,如果混合厂商设备都要纳管,建议在客户端抽象一层“期望状态”模型,把不同厂商的YANG差异挡在外面;第三,如果设备版本跨度大,优先让系统支持“按设备加载YANG集合”,而不是全局用一套模型。这三条决定了你后面是顺畅推进还是天天给设备“打补丁”。
3. 跑通第一个NETCONF会话:华为CE交换机设备侧配置与Python连接
很多人在这个标题的项目上卡住,不是代码不会写,而是设备侧NETCONF服务没打开、SSH用户权限不对,Python端连上去就被踢掉。所以先从设备侧讲,再用ncclient跑通第一个会话,最后用YANG过滤器精准取数。
3.1 设备侧开启NETCONF服务:三条命令把SSH权限收拢
常见做法是登录设备后,在系统视图下创建一个专用SSH用户,并把该用户的service-type指定为snetconf。注意不是普通SSH登录权限,是专门的NETCONF服务类型。命令序列大致如下:
system-view ssh user netadmin ssh user netadmin authentication-type password ssh user netadmin service-type snetconf snetconf server enable quit save这里的逻辑是:先建立SSH用户netadmin,指定密码认证;再把该用户的服务类型限定为snetconf,意思是这个账号只能用来建NETCONF会话,不能登进命令行;最后用snetconf server enable全局开启NETCONF服务。save保存配置,不保存的话设备重启后全部失效。不同版本这条命令名可能有差异,有的版本是netconf server enable,以设备实际支持为准。
3.2 用ncclient建立会话:连接参数逐个说清楚
Python客户端最常用的是ncclient这个库,它实现了RFC 6241客户端逻辑,并且能通过device_params加载厂商能力模型。先pip安装,再写最小连接代码:
from ncclient import manager def connect_ce(host, username, password): """建立到华为CE交换机的NETCONF会话""" return manager.connect( host=host, port=830, username=username, password=password, hostkey_verify=False, look_for_keys=False, allow_agent=False, timeout=30, device_params={"name": "huawei"}, ) with connect_ce("192.168.10.11", "netadmin", "Passw0rd!") as m: result = m.get_config(source="running") print(result.xml[:2000])port=830是NETCONF over SSH的默认端口,如果设备改过端口要同步改。hostkey_verify=False跳过SSH主机密钥校验,避免首次连接时弹出交互确认。look_for_keys和allow_agent都设成False,防止ncclient去自动加载~/.ssh下的密钥或走SSH agent,这在批量脚本里能省掉大量莫名其妙的超时等待。device_params里的huawei是关键,它会让ncclient按华为设备的能力描述解析报文,不加这个参数很多华为设备会上来就报协议错误。
3.3 用YANG过滤器读接口状态:从几MB的XML里只取想要的节点
直接get_config返回的是整机running配置,核心交换机上可能几MB甚至更大,解析慢还占内存。实际项目里几乎都会带filter。过滤器用XML格式,引用设备YANG模型的命名空间,只摘需要的子树。读单个接口状态的写法:
filter_ifm = """ <ifm xmlns="http://www.huawei.com/netconf/v8000/nbr/huawei-ifm"> <interfaces> <interface> <ifName>10GE1/0/1</ifName> </interface> </interfaces> </ifm> """ result = m.get_config(source="running", filter=("subtree", filter_ifm)) print(result.xml)filter用subtree子类型,意思是我给定一个配置子树,设备只返回子树命中的那部分。xmlns里的URL是华为接口管理模块的YANG命名空间,实际设备通告的命名空间可能随版本不同,连上设备后可以用server_capabilities打印出来核对。字符串里的ifName写的是物理接口名,如果型号是CE12800,接口名格式可能变成100GE1/0/1或类似带槽位号的写法,按设备实际显示来填。这个过滤器拼错任何一个节点名,结果都是空XML,不会报错,这是刚开始最容易疑惑的地方。
4. 配置脚本自动下发与收集:模板渲染、批量执行与回滚方案
跑通会话只是开始,系统真正干活的部分在“自动收集”和“自动下发”这两条流水线上。收集是为了建立基线、审计变更、给下发做对比;下发则要考虑模板渲染、执行策略和失败回滚。这两块做好,才算一套能交给非脚本用户的配置管理系统。
4.1 配置收集与差异比对:先把“下发前”和“下发后”讲清楚
收集逻辑很简单:对每台设备执行一次get-config,把XML存成带时间戳和IP的文件。但直接堆原始XML文件后面没法对比,所以要同时落两个东西:原始XML存档和解析后的文本配置。解析这一步,通常是用ElementTree把XML按已知命名空间提取成可读的配置项列表。拿接口配置举例:
import xml.etree.ElementTree as ET ns = {"ifm": "http://www.huawei.com/netconf/v8000/nbr/huawei-ifm"} root = ET.fromstring(result.xml) for iface in root.iter("ifm:interface", ns): name = iface.find("ifm:ifName", ns).text mtu = iface.find("ifm:ifMtu", ns) print(name, mtu.text if mtu is not None else "N/A")注意命名空间必须在ns字典里声明,否则find和iter都匹配不到节点。MTU节点可能不存在,取之前先判空。
有了“下发前”的解析结果,下发后再跑一次同样的收集,就能用difflib做统一格式的文本对比,把变化行直接塞给前端展示。对比代码很简单:
import difflib before = parse_config(before_xml) after = parse_config(after_xml) diff_lines = list(difflib.unified_diff( before.splitlines(), after.splitlines(), lineterm="", n=1, ))这段diff就是图形化界面里“配置变更”页面的数据来源。实际使用中要注意,有些设备会在XML里返回动态状态节点,比如接口当前up/down状态,这类节点每次收集都可能变化,对比时要过滤掉,只比配置节点,否则每次都会误报变更。
4.2 自动下发:Jinja2渲染XML模板,配合edit_config减掉手工步骤
下发端的核心是把“人工构造XML”变成“模板渲染”。我一般用Jinja2做模板,把接口名、MTU、链路模式这些参数暴露成变量,一份模板管一台设备的一个变更任务。典型模板长这样:
from jinja2 import Template tpl = Template(""" <config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> <ifm xmlns="http://www.huawei.com/netconf/v8000/nbr/huawei-ifm"> <interfaces> <interface> <ifName>{{ ifname }}</ifName> <ifLinkMode>{{ mode }}</ifLinkMode> <ifMtu>{{ mtu }}</ifMtu> </interface> </interfaces> </ifm> </config> """) xml_payload = tpl.render(ifname="10GE1/0/1", mode="1", mtu="9100")注意最外层config的xmlns是NETCONF标准命名空间,内层才是华为模型的命名空间,两层namespace不能合并。渲染好之后用edit_config下发:
m.edit_config( target="running", config=xml_payload, error_option="rollback-on-error", )target="running"是因为华为CE交换机默认不启用独立的candidate库,直接改running立即生效,部分框式版本支持candidate但需要单独开启。error_option="rollback-on-error"表示本次下发遇到错误就自动回滚到下发前状态,这是单次任务最重要的保险丝。
4.3 批量执行与回滚:并发数控制、备份与后悔药
批量下发时最忌讳的是把所有设备丢进线程池无脑并发。NETCONF会话本身是长连接,设备侧有会话数上限,20台设备同时建会话,盒式交换机可能直接拒绝服务。我一般用ThreadPoolExecutor限制并发为3到5,另外每台设备执行前必须先拉一次running配置存到备份目录,作为后悔药。
import time from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path def deploy_one(host, username, password, xml_payload, backup_dir): with manager.connect( host=host, port=830, username=username, password=password, hostkey_verify=False, look_for_keys=False, allow_agent=False, timeout=30, device_params={"name": "huawei"}, ) as m: backup = m.get_config(source="running").xml backup_path = Path(backup_dir, f"{host}_{time.strftime('%Y%m%d_%H%M%S')}.xml") backup_path.write_text(backup, encoding="utf-8") try: m.edit_config(target="running", config=xml_payload, error_option="rollback-on-error") except Exception: # 如果设备或版本不支持自动回滚,就用备份覆盖回去 m.copy_config(source=backup, target="running") raise with ThreadPoolExecutor(max_workers=5) as pool: futures = [pool.submit(deploy_one, h, "netadmin", "Passw0rd!", xml_payload, "./backup") for h in hosts] for f in as_completed(futures): f.result()这里先备份再下发,顺序不能反。copy_config第一个参数是备份XML文本,第二个是目标库,效果是整机配置被改写。它是最后的后悔药,但成本也高,会覆盖这期间其他会话改的配置,所以只建议在edit_config失败且自动回滚失效时调用。批量执行完还有一个收尾动作:重新get_config,把结果和备份做diff,生成执行报告。这一步能自动发现“下发显示成功但实际没生效”的情况。
5. 华为CE上跑NETCONF的五个踩坑实录:从连不上到配置不生效
这套方案第一眼看起来不难,真正让新手翻车的细节都藏在设备版本、命名空间、配置依赖和编码里。下面这些坑是我多年经验里最高频的,按“现象→原因→解决”来写,可以直接当排查手册用。
5.1 连接与认证类故障:超时、秒断、批量被打爆
现象一:Python代码connect时直接报timeout或EOF,但设备IP能ping通,SSH普通登录也正常。原因绝大多数是设备侧NETCONF服务没有真正开启,或者SSH用户没有service-type snetconf。解决办法:登录设备检查snetconf server enable有没有配置,再看ssh user的service-type里有没有snetconf,注意这是一个独立的service-type,不是普通SSH权限,重新加一条ssh user netadmin service-type snetconf然后save即可。
现象二:批量脚本跑一半,后面的设备全部连接超时,前面已连接的成功执行完但断开时卡很久。原因是设备NETCONF会话数有上限,线程开太多把设备会话池占满了,且部分连接没有被正常关闭。解决办法:强制用with语句让会话释放,限制并发数量到5以内,在两次批量操作之间加sleep分散建连压力。
5.2 数据与模型类故障:命名空间不匹配、查询结果为空
现象三:get_config返回了XML,但是filter过滤后结果为空,或者报invalid value。原因几乎都是命名空间问题:华为CE的YANG命名空间带版本标识,不同版本设备通告的schema不同,用了一份与设备版本不匹配的YANG文件去拼XML。解决办法:连接后先打印server_capabilities,找到设备实际通告的命名空间,再按这个地址去拼过滤器。以接口模块为例,它通常形如http://www.huawei.com/netconf/v8000/nbr/huawei-ifm,但“通常”不代表所有设备都一样,每次纳管新型号都要先核对一遍。
现象四:edit_config下发报错说某个节点“is not supported”或“does not exist”。原因是设备当前版本/板卡根本不支持这个YANG节点,比如在CE6800上配CE12800才有的集群相关配置。解决办法:查设备该型号的YANG支持清单,把模板里对应的节点去掉,或者按设备型号区分模板集,不搞“一套模板通吃”。
5.3 执行与编码类故障:依赖缺失、特殊字符、生效延迟
现象五:下发接口MTU或链路模式成功后,设备显示配置存在,但业务没有按预期生效,或者下发时直接报依赖错误。原因是配置节点之间存在底层依赖,比如给VLANIF接口配IP之前必须先存在对应VLAN,又比如接口处于shutdown状态时,修改物理参数会被部分版本拒绝。解决办法:下发前用get_config先确认前置节点存在,如果不存在,把前置创建和后续修改合并到同一个config报文里一次提交,让设备按顺序执行。合并的代价是模板变复杂,但比拆两次下发稳定得多。
现象六:XML报文里包含&、<、>这类字符时,Python端渲染的模板在设备侧解析失败,报XML parse error,但肉眼看起来配置内容明明是对的。原因是XML里这些字符必须转义,如果配置项里出现了描述文本、URL或组合命令,漏转义就翻车。解决办法:所有外部输入都经过xml.sax.saxutils.escape处理后再渲染进模板,不要直接把用户输入的描述文本拼进XML字符串。另外还有一个经常被忽略的点:edit_config下发成功不等于配置已保存,设备重启会丢配置,批量任务完成后最好统一执行一次save操作。
6. 图形化客户端与网络拓扑监控:把自动化能力交给不写脚本的人
系统做到命令行能批量跑通,还只是完成了后端的一半。这个标题里特意写了图形化客户端界面和网络拓扑监视,说明目标是让不具备脚本能力的人也能用这套系统。
6.1 客户端形态取舍:单机工具还是Web界面
单机场景我偏向用PyQt写桌面客户端,双击即用,适合个人维护少量设备。但如果团队有多人要看配置变更、要留审计记录,我会直接选Web方案:Flask或FastAPI做后端API,Vue做前端,配置diff结果用统一格式传过去,任务执行状态用WebSocket实时推。每一台下发成功的设备,界面上从“待执行”变成“已生效”,执行失败则直接展示diff对比和回滚时间,整个过程不用碰命令行。这个形态比桌面工具更适合团队落地,也方便后续接LDAP做权限控制。
6.2 拓扑数据从哪来:LLDP邻居采集与推送机制
网络拓扑监控的数据源,优先用设备已有的LLDP邻居表。通过NETCONF读huawei-lldp相关YANG模块,拿到每台设备的邻居关系,落库后前端用关系图渲染。采集频率不用高,5到10分钟一轮就够,但推送机制我强烈建议走WebSocket而不是前端轮询。
# 采集线程把邻居表写入内存队列 topo_queue.put(neighbor_entries) # WebSocket回调里取队列数据推给前端 async def topo_broadcast(websocket, path): while True: data = topo_queue.get() await websocket.send(json.dumps(data))这样前端拓扑图能秒级体现新增、断链,后端采集压力也没增加。验证这套系统是否可靠,我会做一次全链路check:测试环境找一台CE6800,准备好基线配置和一套变更配置,执行一次“备份+下发+回读+diff”,确认备份文件可恢复、diff只包含目标变更、中途拔掉网络能自动失败回滚。这三条通过,再上线也不迟。
我个人吃过最大的一次亏,就是拿了一份旧版YANG文件直接往新版本设备上下发,结果前20台全部超时回滚,现场异常狼狈。现在每接一批新型号设备,第一件事永远是打印server_capabilities存档,再开始写模板和过滤器。这个习惯帮我省掉了大量后面的排查时间,希望帮到你。
本文还有配套的精品资源,点击获取