Sổ tay kỹ thuật

Junos PyEZ cơ bản

how-to#junos#python#pyez#netconf· cập nhật 2026-11-06

Dùng khi viết script Python thao tác Junos qua PyEZ - kết nối, đọc facts, gọi RPC, load config, commit confirmed và rollback.

Sửa trang này
Mục lục

Tóm tắt: PyEZ (junos-eznc) chạy trên NETCONF over SSH (port 830). Dùng Device để kết nối và đọc dev.facts, dev.rpc.* để lấy dữ liệu vận hành, và Config để load/diff/commit. Quy trình an toàn: load → diff → commit_check → commit(confirm=N) → kiểm tra → commit() lần hai để xác nhận.

Luồng hoạt động

flowchart LR A["Device.open()"] --> B["Config(dev, mode=exclusive)"] B --> C["cu.load()"] C --> D["cu.diff()"] D --> E["cu.commit_check()"] E --> F["cu.commit(confirm=5)"] F --> G{"Kiểm tra OK?"} G -->|Có| H["cu.commit() xác nhận"] G -->|Không| I["không làm gì, tự rollback sau 5 phút"]

Cấu hình

Chuẩn bị

set system services netconf ssh
set system login user automation class super-user authentication ssh-ed25519 "ssh-ed25519 AAAA... automation"
python3 -m pip install junos-eznc
export NET_USER=automation NET_PASS='***'

Kết nối và facts

facts.py
import os
from jnpr.junos import Device
 
creds = {"user": os.environ["NET_USER"], "passwd": os.environ["NET_PASS"]}
 
with Device(host="198.51.100.21", port=830, **creds) as dev:
    dev.timeout = 60  # timeout RPC (giây), mặc định 30
    f = dev.facts
    print(f["hostname"], f["model"], f["version"], f["serialnumber"])

Dùng SSH key thay mật khẩu: bỏ passwd, thêm ssh_private_key_file="~/.ssh/id_ed25519". Có ~/.ssh/config (ProxyJump...) thì truyền ssh_config="~/.ssh/config", xem SSH config và jumphost.

Gọi RPC vận hành

Tên RPC lấy bằng | display xml rpc trên CLI, đổi - thành _:

user@pe1> show interfaces terse ge-0/0/1 | display xml rpc
<rpc>
    <get-interface-information>
        <terse/>
        <interface-name>ge-0/0/1</interface-name>
    </get-interface-information>
</rpc>
rsp = dev.rpc.get_interface_information(terse=True, interface_name="ge-0/0/1")
for ifl in rsp.findall(".//logical-interface"):
    print(ifl.findtext("name").strip(), ifl.findtext("oper-status").strip())
 
# Lấy config dạng JSON (đã lọc)
cfg = dev.rpc.get_config(
    filter_xml="<configuration><protocols><bgp/></protocols></configuration>",
    options={"format": "json"},
)

Load, diff, commit confirmed

push_bgp.py
import os
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
from jnpr.junos.exception import ConfigLoadError, CommitError, LockError
 
creds = {"user": os.environ["NET_USER"], "passwd": os.environ["NET_PASS"]}
 
with Device(host="198.51.100.21", **creds) as dev:
    try:
        with Config(dev, mode="exclusive") as cu:
            cu.load(path="bgp.set", format="set")   # hoặc format="text" + merge=True
            diff = cu.diff()
            if not diff:
                print("Không có thay đổi")
            else:
                print(diff)
                cu.commit_check()
                cu.commit(comment="CHG-1236 add peer", confirm=5)
                # ... kiểm tra BGP lên bằng dev.rpc.get_bgp_summary_information() ...
                cu.commit(comment="CHG-1236 confirm")
    except LockError:
        print("Config đang bị khoá bởi session khác")
    except (ConfigLoadError, CommitError) as e:
        print("Lỗi:", e)
bgp.set
set protocols bgp group EBGP-PEER neighbor 203.0.113.1 peer-as 64501
set protocols bgp group EBGP-PEER neighbor 203.0.113.1 description to-transit-a
set protocols bgp group EBGP-PEER import TRANSIT-IN
set protocols bgp group EBGP-PEER export TRANSIT-OUT
Cách load Ý nghĩa
cu.load(path="x.set", format="set") Như load set
cu.load(path="x.conf", format="text", merge=True) Như load merge
cu.load(path="x.conf", format="text", overwrite=True) Như load override (thay toàn bộ config)
cu.load(path="x.conf", format="text") Không merge/overwrite → load replace (theo tag replace:)
cu.load(template_path="t.j2", template_vars={...}, format="set") Render Jinja2 rồi load
cu.load("set system ntp server 192.0.2.123", format="set") Load chuỗi trực tiếp

mode của Config: exclusive (khoá, tương đương configure exclusive), private (candidate riêng), dynamic, batch, ephemeral. Trên router nhiều người cùng thao tác, dùng exclusive hoặc private.

Rollback

with Config(dev, mode="exclusive") as cu:
    cu.rollback(rb_id=1)          # nạp lại config trước commit gần nhất
    print(cu.diff())
    cu.commit(comment="rollback 1")

cu.rollback() (mặc định rb_id=0) là huỷ thay đổi chưa commit trong candidate. cu.rescue(action="reload") nạp lại rescue config.

Lỗi thường gặp

Triệu chứng Nguyên nhân Cách xử lý
ConnectRefusedError Chưa bật system services netconf ssh hoặc port 830 bị filter Bật NETCONF, kiểm tra lo0 filter / mgmt ACL
ConnectAuthError Sai user/mật khẩu hoặc key Thử ssh -p 830 user@host -s netconf
ConnectTimeoutError Không tới được host, sai port Kiểm tra route mgmt, nc -vz host 830
RpcTimeoutError khi commit Commit lâu (config lớn, RE yếu) Tăng dev.timeout = 300
LockError Có người đang configure exclusive / có thay đổi chưa commit status trên CLI, dùng mode="private"
ConfigLoadError (syntax error) Sai cú pháp set hoặc sai hierarchy Thử dòng lệnh trực tiếp trên CLI trước
Commit confirmed tự rollback Quên cu.commit() lần hai trong thời hạn Thêm bước xác nhận sau khi kiểm tra
dev.facts thiếu khoá / chậm Facts tự gom khi open() Device(..., gather_facts=False) nếu không cần

Tham khảo