Sổ tay kỹ thuật

NETCONF với ncclient

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

Dùng khi cần đọc/ghi config IOS XR hoặc Junos qua NETCONF bằng Python ncclient - get-config có filter, edit-config vào candidate, validate, commit và commit confirmed.

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

Tóm tắt: Bật NETCONF trên thiết bị (port 830), kết nối bằng ncclient.manager.connect(..., device_params={"name": "iosxr"|"junos"}). Đọc bằng get_config / get với filter subtree (hoặc xpath nếu thiết bị có capability :xpath). Ghi bằng edit_config(target="candidate") → validate → commit. Luôn khoá candidate trong lúc sửa.

Luồng hoạt động

sequenceDiagram participant P as Python (ncclient) participant R as Router P->>R: hello (capabilities) R-->>P: hello (capabilities) P->>R: lock candidate P->>R: edit-config candidate P->>R: validate candidate P->>R: commit confirmed (timeout 300) P->>R: get (kiểm tra) P->>R: commit (xác nhận) P->>R: unlock candidate

Cấu hình

Bật NETCONF

IOS XR Junos
netconf-yang agent ssh set system services netconf ssh
ssh server netconf vrf default (tuỳ chọn) set system services netconf rfc-compliant
IOS XR
netconf-yang agent
 ssh
!
ssh server netconf vrf default
Junos
set system services netconf ssh

Kiểm tra từ máy quản trị (phải thấy <hello> kèm danh sách capability):

ssh -p 830 automation@198.51.100.11 -s netconf

Kết nối và capability

python3 -m pip install ncclient
export NET_USER=automation NET_PASS='***'
nc_common.py
import os
from ncclient import manager
 
def connect(host: str, platform: str):
    conn = {
        "host": host,
        "port": 830,
        "username": os.environ["NET_USER"],
        "password": os.environ["NET_PASS"],
        "hostkey_verify": False,           # lab; production nên dùng known_hosts
        "device_params": {"name": platform},  # "iosxr" hoặc "junos"
        "timeout": 60,
    }
    return manager.connect(**conn)
 
if __name__ == "__main__":
    with connect("198.51.100.11", "iosxr") as m:
        for cap in m.server_capabilities:
            if "openconfig-interfaces" in cap or ":candidate" in cap or ":xpath" in cap:
                print(cap)

get-config với filter

OC_IF = """
<interfaces xmlns="http://openconfig.net/yang/interfaces">
  <interface>
    <name>GigabitEthernet0/0/0/1</name>
  </interface>
</interfaces>
"""
 
with connect("198.51.100.11", "iosxr") as m:
    # subtree filter: chỉ lấy interface có name khớp
    r = m.get_config(source="running", filter=("subtree", OC_IF))
    print(r.data_xml)
 
    # xpath filter: chỉ khi thiết bị có capability :xpath
    r = m.get(filter=("xpath", "/interfaces/interface[name='GigabitEthernet0/0/0/1']/state"))

Junos: config native nằm dưới <configuration>:

JUNOS_BGP = "<configuration><protocols><bgp/></protocols></configuration>"
 
with connect("198.51.100.21", "junos") as m:
    r = m.get_config(source="running", filter=("subtree", JUNOS_BGP))
    print(r.data_xml)

edit-config → validate → commit

push_desc.py
from nc_common import connect
 
CFG = """
<config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <interfaces xmlns="http://openconfig.net/yang/interfaces">
    <interface>
      <name>GigabitEthernet0/0/0/1</name>
      <config>
        <name>GigabitEthernet0/0/0/1</name>
        <description>to-pe2-hcm</description>
      </config>
    </interface>
  </interfaces>
</config>
"""
 
with connect("198.51.100.11", "iosxr") as m:
    with m.locked("candidate"):
        m.edit_config(target="candidate", config=CFG, default_operation="merge")
        m.validate(source="candidate")
        m.commit(confirmed=True, timeout="300")   # tự rollback sau 300s nếu không xác nhận
        # ... kiểm tra bằng m.get(...) ...
        m.commit()                                # xác nhận

Junos native, cùng quy trình, chỉ khác payload:

junos_desc.xml
<config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <configuration>
    <interfaces>
      <interface>
        <name>ge-0/0/1</name>
        <description>to-pe1-hni</description>
      </interface>
    </interfaces>
  </configuration>
</config>

Junos còn có RPC riêng qua device_params={"name": "junos"}: m.load_configuration(action="set", config=["set interfaces ge-0/0/1 description to-pe1-hni"]) và m.command("show bgp summary", format="text"). Nếu chỉ dùng Junos, PyEZ gọn hơn.

Thao tác theo phần tử

Thuộc tính operation trên phần tử XML Ý nghĩa
merge (mặc định theo default_operation) Gộp vào config hiện có
replace Thay phần tử đó và toàn bộ con
create Tạo mới, lỗi nếu đã tồn tại
delete Xoá, lỗi nếu không tồn tại
remove Xoá, không lỗi nếu không tồn tại

Ví dụ xoá description: <description xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="remove"/>.

Huỷ và lấy lại

Mục đích Lệnh
Bỏ thay đổi chưa commit trong candidate m.discard_changes()
Huỷ commit confirmed đang chờ (NETCONF 1.1) m.cancel_commit()
Đọc candidate để so m.get_config(source="candidate", filter=...)

Lỗi thường gặp

Triệu chứng Nguyên nhân Cách xử lý
SSHError: Could not open socket Chưa bật NETCONF / port 830 bị chặn Bật như trên, nc -vz host 830
AuthenticationException Sai user/pass, hoặc ncclient thử key trong ~/.ssh trước Thêm allow_agent=False, look_for_keys=False
RPCError: lock-denied Session khác giữ candidate Đợi hoặc kill session (show netconf-yang clients trên IOS XR, status trên Junos)
RPCError: unknown-namespace / unknown-element Sai namespace hoặc model không hỗ trợ trên release này So với capability của thiết bị
Filter trả về rỗng Key trong subtree không khớp chính xác, hoặc dùng path state trong get_config get_config chỉ trả config; state dùng m.get()
operation-not-supported với xpath Thiết bị không có :xpath Đổi sang subtree
Commit xong config tự mất Commit confirmed hết hạn, chưa gọi m.commit() lần hai Gọi xác nhận trong thời hạn timeout

Tham khảo