NETCONF & YANG: Automate Network Configs via Python

NETCONF combined with YANG data models gives engineers a structured, programmatic alternative to screen-scraping CLI output.This article walks through using Python’s ncclient library to connect to Cisco IOS-XE and Juniper Junos devices, retrieve operational state, and push configuration changes using proper YANG-modeled payloads.


What You Need Before Starting

This guide assumes you have:

  • Python 3.8+
  • ncclient installed (pip install ncclient)
  • An IOS-XE device (physical or CSR1000v/Catalyst 8000v) with NETCONF enabled
  • A Junos device (physical or vMX) with NETCONF over SSH configured
  • SSH access and credentials for both

Install dependencies:

pip install ncclient lxml

lxml is needed for parsing and building XML payloads, which is what NETCONF speaks underneath.


Enabling NETCONF on Devices

Cisco IOS-XE

configure terminal
 netconf-yang
 netconf-yang feature candidate-datastore
end

Verify it’s listening on TCP 830:

ssh -p 830 admin@192.168.1.1 -s netconf

You should receive a <hello> capabilities exchange. If you don’t, check that ip ssh version 2 is configured and the management VRF is correct.

Juniper Junos

set system services netconf ssh port 830
commit

Junos enables NETCONF over SSH by default on port 830 once this is set. No additional daemon configuration is required.


Connecting with ncclient

ncclient abstracts the NETCONF session negotiation, capability exchange, and RPC framing. Connection parameters differ slightly per vendor.

IOS-XE Connection

from ncclient import manager

ios_xe = manager.connect(
    host="192.168.1.1",
    port=830,
    username="admin",
    password="C1sc0!",
    hostkey_verify=False,
    device_params={"name": "iosxe"}
)

device_params={"name": "iosxe"} tells ncclient which manager handler to load. This adjusts behavior around candidate datastores and commit operations specific to IOS-XE.

Junos Connection

junos = manager.connect(
    host="192.168.1.2",
    port=830,
    username="netconf_user",
    password="Junip3r!",
    hostkey_verify=False,
    device_params={"name": "junos"}
)

Retrieving Configuration with get-config

NETCONF’s <get-config> RPC pulls configuration from a specified datastore (running, candidate, or startup).

Get Interface Config from IOS-XE

The filter below uses a subtree filter scoped to the ietf-interfaces YANG model:

interface_filter = """
<filter xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface>
      <name>GigabitEthernet1</name>
    </interface>
  </interfaces>
</filter>
"""

response = ios_xe.get_config(source="running", filter=interface_filter)
print(response.xml)

The XML response will include the full subtree for GigabitEthernet1 — IP address, description, admin state, and MTU — all structured according to the YANG schema.

Get Interface Config from Junos

Junos uses its own native YANG models in addition to OpenConfig:

junos_filter = """
<filter type="subtree">
  <configuration>
    <interfaces>
      <interface>
        <name>ge-0/0/0</name>
      </interface>
    </interfaces>
  </configuration>
</filter>
"""

response = junos.get_config(source="running", filter=junos_filter)
print(response.xml)

Retrieving Operational State with get

<get> retrieves live operational data, not just configuration. This is equivalent to show commands, but structured.

oper_filter = """
<filter xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <interfaces-state xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface>
      <name>GigabitEthernet1</name>
      <statistics/>
    </interface>
  </interfaces-state>
</filter>
"""

oper_data = ios_xe.get(filter=oper_filter)
print(oper_data.xml)

Parse specific values using lxml:

from lxml import etree

root = etree.fromstring(oper_data.xml.encode())
ns = {
    "nc": "urn:ietf:params:xml:ns:netconf:base:1.0",
    "if": "urn:ietf:params:xml:ns:yang:ietf-interfaces"
}

in_octets = root.find(".//if:in-octets", ns)
if in_octets is not None:
    print(f"Inbound octets: {in_octets.text}")

Pushing Configuration Changes with edit-config

This is where NETCONF becomes operationally useful. You’re sending a structured XML payload that maps directly to a YANG model — no regex, no CLI parsing.

Add a Loopback on IOS-XE

loopback_config = """
<config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface>
      <name>Loopback100</name>
      <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">
        ianaift:softwareLoopback
      </type>
      <enabled>true</enabled>
      <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
        <address>
          <ip>10.100.0.1</ip>
          <prefix-length>32</prefix-length>
        </address>
      </ipv4>
    </interface>
  </interfaces>
</config>
"""

response = ios_xe.edit_config(target="running", config=loopback_config)
print(response.xml)

A successful operation returns <ok/> inside an <rpc-reply>. If the payload is malformed or violates the YANG schema, you’ll receive an <rpc-error> with the error message and path.

Modify an Interface Description on Junos (with Commit)

Junos uses a candidate datastore by default. You must explicitly commit after editing:

junos_edit = """
<config>
  <configuration>
    <interfaces>
      <interface>
        <name>ge-0/0/0</name>
        <description>Uplink to Core</description>
      </interface>
    </interfaces>
  </configuration>
</config>
"""

junos.edit_config(target="candidate", config=junos_edit)
junos.commit()

To validate the candidate before committing:

junos.validate(source="candidate")
junos.commit()

For IOS-XE with candidate datastore enabled, the same pattern applies:

ios_xe.edit_config(target="candidate", config=loopback_config)
ios_xe.validate(source="candidate")
ios_xe.commit()

Deleting Configuration

Use the operation="delete" attribute inside the XML payload to remove config elements:

delete_loopback = """
<config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface operation="delete">
      <name>Loopback100</name>
    </interface>
  </interfaces>
</config>
"""

ios_xe.edit_config(target="running", config=delete_loopback)

Discovering Supported YANG Models

Before you write payloads, confirm which YANG modules the device advertises. These are exchanged in the initial <hello> message.

for capability in ios_xe.server_capabilities:
    if "yang" in capability.lower():
        print(capability)

For IOS-XE, you’ll see entries like:

urn:ietf:params:xml:ns:yang:ietf-interfaces?module=ietf-interfaces&revision=2018-02-20
http://cisco.com/ns/yang/Cisco-IOS-XE-native?module=Cisco-IOS-XE-native&revision=2023-03-01

The revision date matters — YANG models evolve, and a payload built against an older revision may fail against a newer one. Use pyang or Cisco’s YANG Suite to explore model structure offline.


A Note on RESTCONF vs NETCONF

RESTCONF (RFC 8040) exposes the same YANG data models over HTTP/HTTPS using JSON or XML. If your environment already has REST tooling in place, RESTCONF with Python’s requests library can be simpler for read operations:

import requests

url = "https://192.168.1.1/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet1"
headers = {"Accept": "application/yang-data+json"}
response = requests.get(url, auth=("admin", "C1sc0!"), headers=headers, verify=False)
print(response.json())

However, NETCONF remains the better choice for transactional config changes — particularly where candidate datastores, rollback-on-error, and confirmed-commit semantics matter. RESTCONF Python integration works well for read-heavy workflows; ncclient Python NETCONF is the stronger option for full config lifecycle management.


Conclusion

NETCONF YANG network automation removes the ambiguity of CLI scraping and replaces it with schema-validated, transactional configuration management. The patterns shown here — get-config, edit-config, validate, and commit — cover the majority of day-to-day automation tasks on IOS-XE and Junos. The biggest operational hurdle is understanding which YANG models a given platform version supports and at what revision; invest time in pyang and vendor YANG repositories before writing production payloads. Once your model-to-payload mapping is solid, these same Python patterns scale cleanly across dozens or hundreds of devices with minimal modification.

Rick Donato

Want to become a networking expert ?

Here is our hand-picked selection of the best courses you can find online:
Cisco CCNA Certification Gold Bootcamp
Complete Cyber Security Course – Network Security
Internet Security Deep Dive course
Python Pro Bootcamp
and our recommended certification practice exams:
Delta Practice Tests