Junos PyEZ: Python Automation over NETCONF - 夜莺博客

Junos PyEZ: Python Automation over NETCONF

Junos PyEZ is a microframework for managing Junos devices from Python, built on NETCONF rather than screen scraping. That distinction is the whole value: running show interfaces through an SSH library returns text you have to parse and hope about, while PyEZ returns structured facts and tables keyed by field name. The same applies to configuration: PyEZ can lock, load, diff, commit and unlock, with first-class rollback. This article covers the connection model, the operational table interface, safe configuration changes, and the error handling needed to run it unattended.

Install and connect

pip install junos-eznc
# On Debian/Ubuntu you may also need: pip install junos-eznc lxml ncclient
from jnpr.junos import Device
from jnpr.junos.exception import ConnectError

with Device(host='192.0.2.10', user='automation',
            password='secret', port=22) as dev:
    print(dev.facts['hostname'], dev.facts['model'], dev.facts['version'])
    print(dev.facts['serialnumber'])

NETCONF must be enabled on the device (set system services netconf ssh). For fleets, keep credentials out of the source: PyEZ supports a .junos-eznc configuration file, environment variables, and SSH keys, all better than literals. PyEZ also supports connecting through a console server for devices with no management network, which is useful for zero-touch and recovery scenarios.

Operational tables: structured show output

from jnpr.junos.op.ethport import EthPortTable
from jnpr.junos.op.routes import RouteTable
from jnpr.junos.op.arp import ArpTable

with Device(host='192.0.2.10', user='automation', password='secret') as dev:
    eths = EthPortTable(dev).get()
    for name, port in eths.items():
        print(name, port.oper, port.mac)

    for route in RouteTable(dev).get():
        print(route._routing_table, route._prefix, route._nexthop)

    for arp in ArpTable(dev).get():
        print(arp._interface, arp._macip)

Tables give you dictionaries keyed by the natural index of each entry, with attributes named after the fields in the show output. When a table does not expose a field you need, fall back to dev.rpc.get_... calls that return XML, which you can then address with XPath. That combination covers everything the CLI can show without any text parsing.

Safe configuration changes

The configuration utility wraps the lock-load-diff-commit-unlock cycle, which is the part that makes unattended changes survivable.

from jnpr.junos.utils.config import Config
from jnpr.junos.exception import ConfigLoadError, CommitError

with Device(host='192.0.2.10', user='automation', password='secret') as dev:
    cu = Config(dev)
    try:
        cu.lock()
        cu.load('set interfaces ge-0/0/0 description "UPLINK-TO-CORE"',
                format='set', merge=True)
        diff = cu.diff()
        if not diff:
            print("no changes required")
        else:
            print(diff)
            cu.commit(comment="automation: interface description",
                      timeout=120, confirm=5)
            cu.commit_check()
    except (ConfigLoadError, CommitError) as err:
        print("failed:", err)
        cu.rollback(0)
    finally:
        cu.unlock()

Two habits make this production-grade. Use commit(confirm=N) so a change is automatically rolled back unless confirmed within N minutes — the automated equivalent of the safety net described in Junos commit confirmed. And always compute the diff before committing: if the diff is empty, the device already matches the intended state, which is how idempotence is proven rather than assumed.

For templated configuration, PyEZ's Jinja2 loader renders a template against device facts (hostname, model, interface names) so the same template produces correct configuration on routers with different hardware. That removes the most common reason hand-written automation breaks on the third platform variant.

Error handling and scale

from jnpr.junos.exception import (ConnectError, ConnectTimeoutError,
                                  RpcError, LockError, UnlockError)

Distinguish connection problems from device-reported errors when you build retry logic: retry a ConnectTimeoutError, but do not retry an RpcError that says the configuration is invalid — you will just repeat a failing change. In fleet scripts, process devices concurrently but cap the pool size and stagger commits; a hundred simultaneous commits produce a control-plane spike that looks like a device fault. Log every commit with its comment so the configuration change history matches your ticket system, and keep tracing available on the device side as described in Junos traceoptions for troubleshooting.

Where does PyEZ fit against other tools? It is the right choice for Junos-specific depth: tables, RPCs, commit semantics. For a vendor-neutral script that also handles Cisco and Arista, the abstraction in Netmiko network automation trades Junos-specific features for portability. Many teams use both, with Ansible for declarative state and PyEZ for the Junos-specific tasks that no generic module expresses well.

原文链接:https://www.juniper.net/documentation/us/en/software/junos-pyez/junos-pyez-developer/topics/concept/junos-pyez-overview.html