4. Junos PyEZ基础
Junos PyEZ 是 Juniper 官方提供的 Python 库,用于对 Junos 设备进行结构化、可编程的自动化操作。它把设备上的 CLI、NETCONF、RPC 等能力封装成 Python 对象,让运维人员可以用“配置对象 + 表视图 + RPC”的方式管理设备,而不必手工拼接 XML 或反复登录终端。本节围绕 PyEZ 的安装、设备连接、Config 与 Table/View 操作、RPC 调用,以及异常处理与超时控制展开,目标是让读者能写出一个可运行、可排错的 PyEZ 脚本。
安装与环境准备
PyEZ 通过 pip 安装,通常与 Junos 设备管理相关的依赖一起使用。推荐在虚拟环境中安装,避免与系统 Python 包冲突。安装命令如下:
python3 -m venv pyez-env
source pyez-env/bin/activate
pip install junos-eznc
pip install lxml ncclient paramiko
安装完成后,可用 python -c "import jnpr.junos; print(jnpr.junos.__version__)" 验证。PyEZ 依赖 ncclient 与 paramiko 完成 NETCONF over SSH 通信,依赖 lxml 解析 XML。若设备使用较新的 Junos 版本,建议同时确认设备已开启 NETCONF 服务:
set system services netconf ssh port 830
连接前需要准备设备地址、用户名、密码或 SSH 密钥。PyEZ 支持密码认证和密钥认证,生产环境更推荐密钥方式,避免明文密码出现在脚本中。
设备连接:Device 对象与连接参数
PyEZ 的核心入口是 jnpr.junos.Device。它既支持直接传入主机、用户、密码,也支持从 YAML 文件读取连接信息。下面是一个最小连接示例:
from jnpr.junos import Device
dev = Device(host='192.0.2.10', user='netops',
password='ExamplePass', port=830,
timeout=30, gather_facts=True)
dev.open()
print(dev.facts['hostname'])
print(dev.facts['model'])
dev.close()
其中 gather_facts=True 会在连接后自动采集设备基础信息,如主机名、型号、Junos 版本、序列号等,后续做条件判断时非常有用。timeout 控制连接与 RPC 的超时时间,单位是秒。若设备响应较慢或跨广域网管理,应适当调大。连接成功后,dev 对象会持有 NETCONF 会话,所有配置、表视图和 RPC 操作都基于该会话完成。
连接失败常见原因包括:NETCONF 未开启、端口被防火墙拦截、用户名密码错误、SSH 密钥未加载、设备管理地址不可达。排查时先确认 ssh user@host -p 830 能否建立连接,再检查 PyEZ 参数。
Config 操作:加载、提交与回滚
PyEZ 的 Config 类用于管理配置。它支持加载 set 命令、加载 XML 配置、比较候选配置与运行配置、提交以及回滚。下面示例演示用 set 命令方式修改设备描述并提交:
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
dev = Device(host='192.0.2.10', user='netops',
password='ExamplePass').open()
cu = Config(dev)
cu.lock()
cu.load('set system host-name PEZ-LAB-01', format='set')
cu.load('set interfaces ge-0/0/0 description PEZ-TEST',
format='set')
if cu.diff():
print(cu.diff())
cu.commit(comment='PyEZ lab commit', timeout=120)
else:
print('no change')
cu.unlock()
dev.close()
关键点有三个:第一,lock() 与 unlock() 保证配置互斥,避免多人同时改配置;第二,load() 只是把配置放入候选配置,不会立即生效;第三,commit() 才真正提交。diff() 用于查看候选配置与当前运行配置的差异,提交前先看 diff 是良好习惯。若提交后发现异常,可用 cu.rollback(1) 回滚到上一版本,再 commit()。对于批量配置,建议使用 format='xml' 或 format='text' 加载完整配置片段,减少逐条 set 带来的会话开销。
Table 与 View:结构化读取设备状态
PyEZ 的 Table/View 机制用于把设备返回的 XML 状态数据映射成 Python 对象。Table 定义“要取哪张表”,View 定义“每行要哪些字段”。例如读取接口状态:
from jnpr.junos import Device
from jnpr.junos.factory import loadyaml
globals().update(loadyaml('interfaces.yml'))
dev = Device(host='192.0.2.10', user='netops',
password='ExamplePass').open()
intf = InterfacesTable(dev).get()
for item in intf:
print(item.name, item.admin, item.oper, item.mtu)
dev.close()
对应的 YAML 定义示例:
InterfacesTable:
rpc: get-interface-information
args:
terse: True
item: physical-interface
key: name
view: InterfacesView
InterfacesView:
fields:
name: name
admin: admin-status
oper: oper-status
mtu: mtu
Table 通过 rpc 指定底层 RPC,item 指定 XML 中重复的行节点,key 指定主键,view 指定字段映射。这样读取接口、路由、ARP、BGP 邻居等状态时,代码更简洁,也更容易做批量判断。若字段路径较深,可在 View 中使用 XPath 表达式,例如 oper: oper-status 或 addr: address/address。
RPC 调用:直接执行 Junos RPC
当 Table/View 不覆盖所需数据时,可以直接调用 RPC。PyEZ 的 dev.rpc 提供动态方法,方法名对应 Junos RPC 名称,短横线转下划线。例如获取设备运行时间:
rsp = dev.rpc.get_system_uptime_information()
print(rsp.findtext('system-uptime-information/up-time'))
rsp2 = dev.rpc.get_interface_information(terse=True)
print(rsp2.findtext('.//interface-name'))
RPC 返回的是 lxml 元素对象,可用 find、findall、findtext 和 XPath 提取数据。若需要执行 CLI 命令,可用 dev.cli('show version', format='text'),但结构化场景优先使用 RPC,因为返回 XML 更稳定、更易解析。
异常处理与超时控制
PyEZ 常见异常包括 ConnectError、ConfigError、RpcError、TimeoutError 等。生产脚本必须捕获并区分处理,避免一个设备失败导致整批任务中断。示例:
from jnpr.junos import Device
from jnpr.junos.exception import ConnectError, RpcError, TimeoutError
try:
dev = Device(host='192.0.2.10', user='netops',
password='ExamplePass', timeout=20).open()
rsp = dev.rpc.get_software_information()
print(rsp.findtext('.//junos-version'))
except ConnectError as e:
print('连接失败:', e)
except TimeoutError as e:
print('超时:', e)
except RpcError as e:
print('RPC 错误:', e)
finally:
if 'dev' in locals() and dev.connected:
dev.close()
超时控制分两层:连接超时由 Device(timeout=...) 控制;单次 RPC 超时可在调用时传入 dev.rpc.get_xxx(timeout=60)。对于批量任务,建议为每台设备设置独立超时,并配合重试次数。重试时不要盲目重放配置提交,应先判断是否已提交成功,避免重复变更。
一个可复用的连接与配置检查片段
把上述要点组合起来,可以得到一个较完整的脚本骨架:连接设备、采集 facts、读取接口表、按需加载配置、提交前看 diff、异常分类处理、最后关闭会话。实际使用时,建议把设备清单、用户名、超时、重试次数放入 YAML 或环境变量,避免硬编码。对于批量设备,逐台串行执行虽然简单,但效率较低;可结合线程池并发,但要注意每台设备独立会话、独立超时和独立异常处理,避免共享 Device 对象。
diff() 并确认变更范围;批量任务中不要因为单台设备超时就重复提交,应先判断提交状态,再决定是否回滚或重试。掌握本节内容后,读者应能独立完成 PyEZ 环境安装、设备连接、配置加载与提交、接口状态表读取、RPC 调用以及异常与超时处理。这些能力是后续编写批量巡检、配置合规、故障自愈等自动化脚本的基础。