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 |
netconf-yang agent
ssh
!
ssh server netconf vrf defaultset system services netconf sshKiể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 netconfKết nối và capability
python3 -m pip install ncclient
export NET_USER=automation NET_PASS='***'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
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ậnJunos native, cùng quy trình, chỉ khác payload:
<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 |