3. Junos XML API与NETCONF协议
Junos 从设计之初就把“可编程”写进了内核:所有 CLI 命令在设备内部都会被转换成 XML 请求,交给管理进程处理,再把结果以 XML 返回。这意味着 CLI 能做的事,XML API 几乎都能做,而且返回的是结构化数据,不需要再用正则去“抠”文本。理解这条链路,是后续用 Python、Ansible、ncclient 做自动化的前提。
XML API 的请求与响应结构
Junos XML API 有两种调用方式:一种是通过 SSH 登录后执行 xml-mode,另一种是直接向 HTTP/HTTPS 端口发送 XML。无论哪种方式,请求体都遵循同一套骨架:最外层是 <rpc> 元素,内部放具体操作,常见的有 <get-configuration>(读配置)、<load-configuration>(下发配置)、<get-interface-information>(读运行状态)、<command>(执行操作命令)。
例如要读取接口 ge-0/0/0 的运行状态,请求可以写成:
<rpc>
<get-interface-information>
<interface-name>ge-0/0/0</interface-name>
</get-interface-information>
</rpc>
设备返回的响应同样以 XML 组织,根元素通常是 <rpc-reply>,内部包含 <interface-information>、<physical-interface>、<logical-interface> 等层级,字段如 <oper-status>up</oper-status>、<admin-status>up</admin-status> 都是独立标签。相比 CLI 的表格输出,XML 的层级关系明确,程序可以直接按标签路径取值,稳定性高得多。
需要特别注意:<rpc> 请求默认是“读”操作,若要修改配置,必须使用 <load-configuration> 并配合 action="merge" 或 action="replace",同时用 <configuration> 包裹配置片段。修改后还要显式提交,否则配置只停留在 candidate 中。
NETCONF over SSH
NETCONF 是 IETF 标准化的网络配置协议,Junos 对其支持非常完整。NETCONF 会话建立在 SSH 之上,默认使用 830 端口,也可以配置为复用 22 端口。与裸 XML API 相比,NETCONF 多了几层规范:消息用 <hello> 交换能力集,用 <rpc> 和 <rpc-reply> 通信,用 <ok> 或 <rpc-error> 表示执行结果,并且支持 <candidate>、<confirmed-commit>、<validate> 等标准操作。
Junos 在 NETCONF 会话建立时会声明自己支持的能力,例如 urn:ietf:params:netconf:base:1.0、urn:ietf:params:netconf:capability:candidate:1.0、urn:ietf:params:netconf:capability:confirmed-commit:1.0,以及 Junos 私有的 urn:juniper:params:xml:ns:junos。这些能力决定了客户端能用哪些操作。比如只有声明了 candidate 能力,客户端才能使用 <edit-config> 直接改 candidate 配置。
NETCONF 的典型交互顺序是:SSH 建连 → 交换 hello → 客户端发送 <get-config> 或 <edit-config> → 服务端返回 <rpc-reply> → 客户端发送 <commit> 使配置生效。整个过程是请求-响应式的,每条消息以 ]]>]]> 作为结束分隔符。
Junos YANG 模型
YANG 是描述配置和状态数据的建模语言。Junos 早期使用私有 XML Schema,从 Junos 16.1 起逐步提供原生 YANG 模型,覆盖接口、路由、防火墙、VLAN 等常见模块。YANG 模型定义了每个节点的路径、类型、是否可写、默认值以及约束条件。例如接口配置在 YANG 中对应 /interfaces/interface/name 和 /interfaces/interface/unit 这样的路径。
YANG 的价值在于:客户端可以先用 <get-schema> 拉取模型,再根据模型自动生成请求,而不必硬编码 XML 标签。Junos 同时支持 OpenConfig YANG 和原生 YANG,前者更通用,后者更贴近 Junos 自身配置层级。实际项目中,如果只针对 Juniper 设备,用原生 YANG 往往更直接;如果要做多厂商统一,OpenConfig 更合适。
使用 ncclient 进行设备交互
ncclient 是 Python 生态中最常用的 NETCONF 客户端库,封装了 SSH 连接、hello 交换、RPC 编解码等细节。安装后即可用几行代码连接 Junos 设备。下面是一个读取接口配置并修改描述的示例:
from ncclient import manager
with manager.connect(
host="192.0.2.10",
port=830,
username="automation",
password="Juniper123",
hostkey_verify=False,
device_params={"name": "junos"}
) as m:
# 读取接口配置
cfg = m.get_config(source="running",
filter=("subtree",
"<configuration><interfaces/></configuration>"))
print(cfg.xml)
# 修改 ge-0/0/0 描述
edit = """
<configuration>
<interfaces>
<interface>
<name>ge-0/0/0</name>
<description>uplink-to-core</description>
</interface>
</interfaces>
</configuration>
"""
m.edit_config(target="candidate", config=edit)
m.commit()
这段代码中,device_params={"name": "junos"} 很关键,它让 ncclient 按 Junos 的方言处理 RPC,避免因命名空间差异导致失败。get_config 的 filter 参数使用 subtree 过滤,只取 interfaces 子树,减少返回数据量。edit_config 写入 candidate 后必须 commit,否则配置不会生效。如果希望更安全,可以用 confirmed=True 配合超时,让设备在指定时间内未收到确认就自动回滚。
import logging; logging.basicConfig(level=logging.DEBUG),这样能看到完整的 XML 请求和响应,便于定位命名空间或标签错误。请求-响应链路与关键判断
把上述内容串起来,一次完整的自动化交互可以概括为:客户端通过 SSH 连接 830 端口,交换 hello 确认能力,发送 <rpc> 请求,Junos 管理进程解析后操作配置库或读取状态,再以 <rpc-reply> 返回。客户端根据返回中是否出现 <ok/> 或 <rpc-error> 判断成功与否。若返回错误,常见原因包括:命名空间不匹配、路径不存在、权限不足、candidate 未提交等。
从工程实践看,建议把“连接、读取、修改、提交、校验”拆成独立函数,并在每次修改前先 get_config 备份 running 配置。对于批量设备,可以用 confirmed-commit 加 60 秒超时,确认无误后再发 commit 确认,避免误配置导致断网。掌握 XML API 与 NETCONF 的这套结构后,后续无论是写 Python 脚本、接入 Ansible,还是对接 Juniper 的 PyEZ,底层逻辑都是一致的。