LAB 4 - NETCONF + YANG Models (get/get-config/edit-config/delete-config)

Readings and Videos to Prepare

These readings and videos will introduce you to important concepts and details which will help you complete this lab activity:

You do not need to follow-along or complete any activities from these resources but you should watch/read them to prepare for the remainder of this lab.

Learning Objectives

By the end of this lab, you will:

  • Configure NETCONF on Cisco IOS-XE devices via CLI

  • Understand the differences between NETCONF and RESTCONF

  • Use Python and ncclient to connect to network devices via NETCONF

  • Retrieve device configuration and operational state using NETCONF get and get-config

  • Understand NETCONF capabilities and how they differ from RESTCONF

  • Use NETCONF edit-config to create, modify, and delete configuration

  • Work with XML data structures instead of JSON

  • Understand NETCONF datastores (running, candidate, startup)

  • Use XPath and subtree filtering to retrieve specific configuration

  • Create new interfaces and IP addresses using NETCONF

  • Modify existing interface configurations

  • Delete interfaces from network devices

  • Compare NETCONF operations directly with equivalent RESTCONF operations from Lab 3

  • Correlate NETCONF XML data structures with CLI commands and RESTCONF JSON

Purpose

Connection to Lab 3: In Lab 3, you learned RESTCONF - a REST API using HTTP methods and JSON. In this lab, you’ll learn NETCONF - an older, more feature-rich protocol using RPC (Remote Procedure Call), XML, and SSH transport.

You’ll perform the same tasks (create interfaces, modify configs, delete interfaces) using NETCONF instead of RESTCONF. This prepares you for Lab 5, where you’ll compare both methods side-by-side and learn when to use each.

This lab teaches NETCONF, a standards-based protocol for network device configuration and management. NETCONF offers several advantages over RESTCONF:

  • Transaction support: Changes can be made to a candidate configuration and committed/rolled back as a unit

  • More mature protocol: NETCONF (RFC 6241) predates RESTCONF (RFC 8040) by several years

  • Broader vendor support: Many vendors support NETCONF but not RESTCONF

  • Configuration validation: Test configurations before committing

  • Better error handling: More detailed error messages with error paths

However, NETCONF also has trade-offs:

  • XML complexity: XML is more verbose than JSON

  • Steeper learning curve: RPC model vs. familiar REST/HTTP

  • Requires different tools: ncclient library instead of requests

You will:

  • Connect to devices using NETCONF over SSH (port 830)

  • Retrieve configuration using get-config with filters

  • Create interfaces using edit-config with XML payloads

  • Compare XML data structures with the JSON you used in Lab 3

  • Understand when NETCONF is preferred over RESTCONF

Prerequisites

You must complete Lab 3 before starting this lab. You should be comfortable with:

  • YANG models and how they define data structures (from Lab 3)

  • Creating interfaces and IP configurations (CCNA knowledge)

  • Python dictionaries, lists, XML parsing concepts

  • The requests library and REST API concepts (from Labs 2-3)

New skills you’ll learn:

  • The ncclient Python library for NETCONF

  • Working with XML data instead of JSON

  • NETCONF RPC operations vs. HTTP methods

  • XPath and subtree filtering

You will need:

  • Python 3.7 or later with ncclient library installed (pip install ncclient)

  • Console access to Cisco 4331 routers and 3650 switches

  • The devices will start with no configuration - you will configure them from scratch

  • Understanding that NETCONF works with datastores (running, candidate, startup)

Library Installation:

pip install ncclient

ncclient provides a Python interface to NETCONF operations. It handles the SSH connection, XML message framing, and NETCONF protocol details.

Lab Environment & Device Access

This lab uses the same physical environment as Lab 1:

  • Cisco 4331 Routers - R1 10.10.20.10/24, R2 10.10.20.20/24 on GigabitEthernet0/0/1

  • Cisco 3650 Switches - SW1 10.10.20.30/24, SW2 10.10.20.40/24 on VLAN 1

All devices run Cisco IOS-XE and start with no configuration. You will:

  1. Console into each device

  2. Configure management interfaces and IP addresses

  3. Enable NETCONF (instead of RESTCONF)

  4. Create user accounts for API access

  5. Test connectivity via Python with ncclient

Network Details:

  • Management VLAN: 10.10.20.0/24

  • Default Gateway: 10.10.20.1 (provided by instructor)

  • Your workstation IP: Assigned by instructor (e.g., 10.10.20.5/24)

  • NETCONF Port: 830 (SSH) vs. RESTCONF Port: 443 (HTTPS)

Section 1 - Initial Device Configuration via CLI

Before you can use NETCONF, you must configure the devices to enable the protocol. Much of this is identical to Lab 3, but we’ll enable NETCONF instead of RESTCONF.

Reference Lab 3: If you recently completed Lab 3 and your devices are still configured, you may only need to add the netconf-yang command. However, this lab assumes you’re starting from scratch.

Time Saver: If starting fresh, you can enable both RESTCONF (from Lab 3) and NETCONF simultaneously. This allows you to practice both protocols without reconfiguring devices.

Step 1: Console Access and Basic Configuration

Connect to each device via console cable. The initial configuration is identical to Lab 3 through the SSH setup.

Cisco 4331 Router Initial Configuration

! Press Enter to get to Router> prompt
Router> enable
Router# configure terminal

! Set hostname
Router(config)# hostname R1

! Disable DNS lookup
Router(config)# no ip domain-lookup

! Set domain name (required for SSH)
Router(config)# ip domain-name lab.local

! Create privileged user for NETCONF access
Router(config)# username admin privilege 15 secret Cisco123!

! Console line configuration
Router(config)# line console 0
Router(config-line)# logging synchronous
Router(config-line)# exec-timeout 0 0
Router(config-line)# exit

! Configure management interface
Router(config)# interface GigabitEthernet0/0/1
Router(config-if)# description Management Interface
Router(config-if)# ip address 10.10.20.10 255.255.255.0
Router(config-if)# no shutdown
Router(config-if)# exit

! Default gateway
Router(config)# ip route 0.0.0.0 0.0.0.0 10.10.20.1

! Generate RSA keys for SSH (required for NETCONF)
Router(config)# crypto key generate rsa modulus 2048
! Type "yes" when prompted

! Enable SSH version 2
Router(config)# ip ssh version 2

! Configure VTY lines for SSH
Router(config)# line vty 0 15
Router(config-line)# transport input ssh
Router(config-line)# login local
Router(config-line)# exit

Verify Network Connectivity

R1(config)# do show ip interface brief

Expected output:

Interface              IP-Address      OK? Method Status                Protocol
GigabitEthernet0/0/1   10.10.20.10     YES manual up                    up

Test from your workstation:

ping -c 3 10.10.20.10

Repeat the same steps on R2 using IP 10.10.20.20/24 on GigabitEthernet0/0/1.

Step 2: Enable NETCONF on the Router

This is the key difference from Lab 3. Instead of enabling RESTCONF, we enable NETCONF:

! Enable NETCONF-YANG on port 830 (default)
R1(config)# netconf-yang

! Optional: Enable candidate datastore support (advanced NETCONF feature)
R1(config)# netconf-yang feature candidate-datastore

NETCONF vs RESTCONF commands:

  • Lab 3 RESTCONF: ip http secure-server and restconf

  • Lab 4 NETCONF: netconf-yang

You can enable both simultaneously if you want to compare them directly!

Step 3: Verify NETCONF is Running

Use the following command to verify NETCONF is active:

R1# show platform software yang-management process

Expected output should show:

confd            : Running
nesd             : Running
syncfd           : Running
ncsshd           : Running    <-- This is the NETCONF SSH daemon
dmiauthd         : Running
nginx            : Running
ndbmand          : Running
pubd             : Running

Look for ncsshd (NETCONF SSH daemon) with status "Running".

Troubleshooting: If NETCONF doesn’t show as running:

  1. Verify SSH is working: show ip ssh

  2. Check RSA keys exist: show crypto key mypubkey rsa

  3. Re-enter the command: netconf-yang

  4. Wait 30-60 seconds for the process to start

Step 4: Configure the Switch (SW1)

Repeat the same configuration on the Cisco 3650 switch:

! Basic config (hostname, domain, user, SSH)
Switch> enable
Switch# configure terminal
Switch(config)# hostname SW1
Switch(config)# no ip domain-lookup
Switch(config)# ip domain-name lab.local
Switch(config)# username admin privilege 15 secret Cisco123!
Switch(config)# crypto key generate rsa modulus 2048
Switch(config)# ip ssh version 2

! Configure management VLAN
Switch(config)# interface vlan 1
Switch(config-if)# description Management Interface
Switch(config-if)# ip address 10.10.20.30 255.255.255.0
Switch(config-if)# no shutdown
Switch(config-if)# exit

! VTY lines
Switch(config)# line vty 0 15
Switch(config-line)# transport input ssh
Switch(config-line)# login local
Switch(config-line)# exit

! Enable NETCONF
Switch(config)# netconf-yang
Switch(config)# netconf-yang feature candidate-datastore

! Save configuration
Switch(config)# end
Switch# write memory

Repeat the same steps on SW2 using IP 10.10.20.40/24 on VLAN 1.

Step 5: Test Network Connectivity

From your workstation, verify connectivity:

ping -c 3 10.10.20.10  # R1
ping -c 3 10.10.20.30  # SW1

Check NETCONF port accessibility (optional):

nc -zv 10.10.20.10 830  # Test if port 830 is open on R1

Expected output: Connection to 10.10.20.10 830 port [tcp/*] succeeded!

Step 6: Install ncclient and Test NETCONF Connection

On your workstation, install the ncclient library:

pip install ncclient

Create a test script to verify NETCONF connectivity:

# test_netconf_connection.py
# Test NETCONF connectivity to router

from ncclient import manager
import sys

# Device connection parameters
device = {
    'host': '10.10.20.10',
    'port': 830,
    'username': 'admin',
    'password': 'Cisco123!',
    'hostkey_verify': False  # Disable SSH key verification for lab
}

# Connect to device via NETCONF
try:
    print(f"Connecting to {device['host']} via NETCONF...")

    # Establish NETCONF session
    with manager.connect(**device, device_params={'name': 'iosxe'}) as m:
        print("OK: NETCONF connection successful!")

        # Display NETCONF capabilities (what the device supports)
        print("\nDevice NETCONF Capabilities:")
        for capability in m.server_capabilities:
            # Print only YANG model capabilities (filter out base protocol capabilities)
            if 'module=' in capability:
                # Extract just the module name
                module_name = capability.split('module=')[1].split('&')[0]
                print(f"  - {module_name}")

        print(f"\nOK: Device supports {len(list(m.server_capabilities))} capabilities")

except Exception as e:
    print(f"ERROR: Connection failed: {e}")
    sys.exit(1)

Run the test:

python test_netconf_connection.py

Expected output:

Connecting to 10.10.20.10 via NETCONF...
OK: NETCONF connection successful!

Device NETCONF Capabilities:

  - ietf-interfaces
  - ietf-ip
  - ietf-netconf
  - Cisco-IOS-XE-native
  - Cisco-IOS-XE-vlan
  ... (many more)

OK: Device supports 150+ capabilities

What just happened?

  1. ncclient established an SSH connection to port 830

  2. NETCONF protocol handshake occurred

  3. Device sent its list of supported YANG models (capabilities)

  4. You’re now ready to send NETCONF operations!

This is analogous to Lab 3’s test where you made an HTTP GET request to RESTCONF. But NETCONF uses SSH transport and capabilities negotiation instead.

Step 7: Save Device Configuration

Save the running configuration on both devices:

R1# write memory
SW1# write memory

Section 1 Complete! Your devices are now accessible via NETCONF. You’re ready to start automating them with Python.

Section 2 - Understanding NETCONF and YANG (Critical Theory Section)

Comparison to Lab 3: In Lab 3, you learned RESTCONF - a REST API using HTTP and JSON. Now you’re learning NETCONF - a protocol using RPC operations, SSH transport, and XML.

Key insight: Both RESTCONF and NETCONF expose the same YANG models. The data structures are identical; only the protocol and data format differ.

NETCONF (Network Configuration Protocol) is an IETF standard protocol (RFC 6241) for network device configuration and management. It predates RESTCONF and offers more advanced features.

2.1 NETCONF vs RESTCONF Comparison

Feature NETCONF RESTCONF

Standard

RFC 6241 (2011)

RFC 8040 (2017)

Transport

SSH (port 830)

HTTPS (port 443)

Data Format

XML

JSON or XML

Operations

RPC-based (get-config, edit-config, delete-config)

REST/HTTP methods (GET, PUT, PATCH, DELETE)

Authentication

SSH username/password or keys

HTTP Basic Auth or token

Datastores

running, candidate, startup

Only running

Transactions

Yes (commit/rollback with candidate)

No

Configuration Validation

Yes (validate before commit)

Limited

Maturity

Older, widely supported

Newer, growing support

Vendor Support

Nearly universal

Cisco, Juniper, some others

Learning Curve

Steeper (XML, RPC concepts)

Easier (familiar REST/JSON)

Python Library

ncclient

requests (standard HTTP library)

When to use NETCONF: * Need transaction support (commit/rollback) * Working with multiple vendors (broader support) * Need to test configurations before applying (candidate datastore) * Require detailed error reporting * Need persistent connections for telemetry streaming

When to use RESTCONF: * Prefer JSON and REST APIs * Working with devices that support both (Cisco IOS-XE) * Building web-based automation (HTTPS native) * Team more familiar with REST than XML/RPC

2.2 NETCONF Architecture

NETCONF operates as a client-server protocol with four layers:

+---------------------+
|   Configuration     |  <-- What you want to change
|   Operations        |      (interfaces, VLANs, routing)
+---------------------+
|   RPC Operations    |  <-- <edit-config>, <get-config>, <delete-config>
+---------------------+
|   Messages Layer    |  <-- XML encoding
+---------------------+
|   Transport (SSH)   |  <-- Secure connection, port 830
+---------------------+

2.3 NETCONF Datastores

Unlike RESTCONF (which only accesses running-config), NETCONF supports multiple datastores:

Datastore Description

running

Active configuration currently running on device (same as RESTCONF)

candidate

Temporary configuration you can build and test before committing to running

startup

Configuration loaded when device boots (same as startup-config in CLI)

Typical workflow with candidate datastore:

  1. Lock candidate datastore

  2. Edit candidate configuration (make changes)

  3. Validate candidate configuration

  4. Commit candidate to running (or discard if invalid)

  5. Unlock candidate datastore

This provides transactional behavior: all changes succeed together or all fail together.

2.4 NETCONF RPC Operations

NETCONF uses RPC (Remote Procedure Call) operations instead of HTTP methods:

NETCONF Operation RESTCONF Equivalent Purpose Datastore

<get>

GET (operational data)

Retrieve operational state (live data)

running

<get-config>

GET (config data)

Retrieve configuration

running/candidate/startup

<edit-config>

PUT/PATCH/POST

Modify configuration

running/candidate

<delete-config>

DELETE

Remove configuration

startup/candidate

<copy-config>

(none)

Copy entire config between datastores

any

<lock> / <unlock>

(none)

Lock datastore for exclusive access

any

<commit>

(none)

Commit candidate to running

candidate → running

<validate>

(none)

Validate configuration without applying

candidate

In this lab, you’ll focus on:

  • get-config - retrieve interface configurations

  • edit-config - create and modify interfaces

  • delete-config or edit-config with delete operation - remove interfaces

2.5 NETCONF XML Structure

NETCONF operations use XML messages. Here’s an example <get-config> RPC to retrieve all interfaces:

<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get-config>
    <source>
      <running/>
    </source>
    <filter>
      <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>
    </filter>
  </get-config>
</rpc>

Breaking it down:

  • <rpc> - wrapper for all NETCONF operations

  • message-id - unique identifier for tracking request/response

  • <get-config> - the operation we’re performing

  • <source> - which datastore (running, candidate, or startup)

  • <filter> - what data to retrieve (analogous to RESTCONF URL path)

  • xmlns - XML namespace (equivalent to YANG module name)

The good news: ncclient handles most XML construction for you!

2.6 YANG Models: Same Data, Different Format

Remember the ietf-interfaces YANG model from Lab 3? It defines the same data structure for both RESTCONF and NETCONF:

Lab 3 RESTCONF JSON:

{
  "ietf-interfaces:interface": {
    "name": "Loopback99",
    "type": "iana-if-type:softwareLoopback",
    "enabled": true,
    "ietf-ip:ipv4": {
      "address": [
        {
          "ip": "192.0.2.99",
          "netmask": "255.255.255.0"
        }
      ]
    }
  }
}

Lab 4 NETCONF XML (same data):

<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
  <name>Loopback99</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>192.0.2.99</ip>
      <netmask>255.255.255.0</netmask>
    </address>
  </ipv4>
</interface>

Notice:

  • JSON uses {} and [], XML uses <tags>

  • JSON keys become XML element names

  • Namespaces are more explicit in XML (xmlns=)

  • The underlying data structure (from YANG) is identical

2.7 NETCONF Filtering

To retrieve specific data (instead of entire config), NETCONF supports two filtering methods:

1. Subtree Filtering (easier, used in this lab):

<filter>
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface>
      <name>GigabitEthernet0/0/1</name>
    </interface>
  </interfaces>
</filter>

This retrieves only the GigabitEthernet0/0/1 interface.

2. XPath Filtering (more powerful, advanced):

<filter type="xpath" select="/interfaces/interface[name='GigabitEthernet0/0/1']"/>

We’ll use subtree filtering in this lab because it’s simpler and ncclient provides helper methods.

2.8 Error Handling in NETCONF

NETCONF provides detailed error responses:

<rpc-reply message-id="101">
  <rpc-error>
    <error-type>application</error-type>
    <error-tag>invalid-value</error-tag>
    <error-severity>error</error-severity>
    <error-message>Interface name already exists</error-message>
    <error-path>/interfaces/interface[name='Loopback99']</error-path>
  </rpc-error>
</rpc-reply>

This is more detailed than RESTCONF HTTP status codes and includes the exact path where the error occurred.

Section 3 - NETCONF get-config: Reading Configuration via Python

Now you’ll use ncclient to retrieve interface configuration from the router.

Comparison to Lab 3 Section 2: In Lab 3, you used requests.get() with RESTCONF URLs. Here, you’ll use manager.get_config() with XML filters. The data retrieved is the same, just in XML format instead of JSON.

3.1 Basic get-config: Retrieve All Interfaces

Create a Python script to retrieve all interfaces:

# netconf_get_interfaces.py
# Retrieve all interface configurations using NETCONF get-config

from ncclient import manager
import xmltodict  # pip install xmltodict (converts XML to Python dict)
import json

# Device connection parameters
device = {
    'host': '10.10.20.10',
    'port': 830,
    'username': 'admin',
    'password': 'Cisco123!',
    'hostkey_verify': False
}

# Define the filter to retrieve interfaces (subtree filtering)
# This specifies we want data from the ietf-interfaces YANG model
filter_interfaces = '''
<filter>
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>
</filter>
'''

# Connect to device and retrieve configuration
with manager.connect(**device, device_params={'name': 'iosxe'}) as m:
    print("Connected to device via NETCONF\n")

    # Send get-config RPC to retrieve running configuration
    # source='running' specifies the running-config datastore
    netconf_reply = m.get_config(source='running', filter=filter_interfaces)

    # The reply is an XML object, convert to string for viewing
    print("Raw XML Response:")
    print(netconf_reply)
    print("\n" + "="*80 + "\n")

    # Convert XML to Python dictionary for easier parsing
    # xmltodict.parse() converts XML to a nested dict structure
    xml_dict = xmltodict.parse(netconf_reply.xml)

    # Pretty-print the dictionary as JSON for readability
    print("Parsed as Python Dictionary (JSON format):")
    print(json.dumps(xml_dict, indent=2))

Run the script:

python netconf_get_interfaces.py

Expected Output (abbreviated):

{
  "rpc-reply": {
    "@xmlns": "urn:ietf:params:xml:ns:netconf:base:1.0",
    "@message-id": "urn:uuid:...",
    "data": {
      "interfaces": {
        "@xmlns": "urn:ietf:params:xml:ns:yang:ietf-interfaces",
        "interface": [
          {
            "name": "GigabitEthernet0/0/0",
            "type": "iana-if-type:ethernetCsmacd",
            "enabled": "false"
          },
          {
            "name": "GigabitEthernet0/0/1",
            "type": "iana-if-type:ethernetCsmacd",
            "enabled": "true",
            "ipv4": {
              "@xmlns": "urn:ietf:params:xml:ns:yang:ietf-ip",
              "address": {
                "ip": "10.10.20.10",
                "netmask": "255.255.255.0"
              }
            }
          }
        ]
      }
    }
  }
}

Understanding the Response:

  • rpc-reply - wrapper for NETCONF responses

  • data - contains the actual configuration data

  • interfaces/interface - list of interfaces (from YANG model)

  • Each interface has name, type, enabled, and optional ipv4

This is the same data you got from RESTCONF in Lab 3, just in XML/dict format instead of JSON!

3.2 Filtering for a Specific Interface

Retrieve only GigabitEthernet0/0/1:

# netconf_get_specific_interface.py
# Retrieve a single interface using subtree filtering

from ncclient import manager
import xmltodict
import json

device = {
    'host': '10.10.20.10',
    'port': 830,
    'username': 'admin',
    'password': 'Cisco123!',
    'hostkey_verify': False
}

# Filter for a specific interface by name
# The filter structure matches the YANG model hierarchy
filter_specific = '''
<filter>
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface>
      <name>GigabitEthernet0/0/1</name>
    </interface>
  </interfaces>
</filter>
'''

with manager.connect(**device, device_params={'name': 'iosxe'}) as m:
    # Get configuration with specific filter
    netconf_reply = m.get_config(source='running', filter=filter_specific)

    # Parse XML to dictionary
    xml_dict = xmltodict.parse(netconf_reply.xml)

    # Navigate to the interface data
    interface_data = xml_dict['rpc-reply']['data']['interfaces']['interface']

    print("Interface Name:", interface_data['name'])
    print("Interface Type:", interface_data['type'])
    print("Enabled:", interface_data['enabled'])

    # Check if interface has IP address
    if 'ipv4' in interface_data:
        ip_info = interface_data['ipv4']['address']
        print(f"IP Address: {ip_info['ip']}")
        print(f"Netmask: {ip_info['netmask']}")
    else:
        print("No IP address configured")

3.3 Verify NETCONF Data via CLI

Critical: Just like in Lab 3, verify that NETCONF data matches the CLI:

R1# show ip interface brief

Expected output:

Interface              IP-Address      OK? Method Status                Protocol
GigabitEthernet0/0/0   unassigned      YES unset  administratively down down
GigabitEthernet0/0/1   10.10.20.10     YES manual up                    up

Compare:

  • NETCONF shows GigabitEthernet0/0/1 with IP 10.10.20.10 and enabled: true

  • CLI shows the same interface as up with IP 10.10.20.10

  • NETCONF enabled: false = CLI administratively down

The data is identical, proving NETCONF retrieves the actual device configuration.

3.4 Practice Exercises

Modify the scripts to:

  1. Retrieve all interfaces and print only interface names:

    • Extract interface_data['name'] for each interface

    • Print in a simple list format

  2. Count how many interfaces are enabled vs. disabled:

    • Loop through interfaces

    • Check enabled field (true/false)

    • Print summary: "5 enabled, 3 disabled"

  3. Find which interface has IP 10.10.20.10:

    • Loop through interfaces

    • Check if ipv4 key exists

    • Print the interface name when IP matches

After each exercise, verify results with CLI commands:

  • show ip interface brief

  • show interfaces status

  • show interface description

Section 4 - NETCONF edit-config: Creating New Interfaces

Now you’ll use NETCONF to create a new loopback interface with an IP address, just like you did with RESTCONF PUT in Lab 3 Section 3.

Comparison to Lab 3: In Lab 3, you used requests.put() with JSON payload. Here, you’ll use manager.edit_config() with XML payload. The result is the same: a new interface created on the device.

4.1 Create Loopback99 with IP Address

# netconf_create_interface.py
# Create a new loopback interface using NETCONF edit-config

from ncclient import manager

device = {
    'host': '10.10.20.10',
    'port': 830,
    'username': 'admin',
    'password': 'Cisco123!',
    'hostkey_verify': False
}

# XML payload to create Loopback99 with IP 192.0.2.99/24
# This uses the same YANG models as Lab 3 (ietf-interfaces, ietf-ip)
config_loopback = '''
<config>
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface>
      <name>Loopback99</name>
      <description>Created via NETCONF</description>
      <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>192.0.2.99</ip>
          <netmask>255.255.255.0</netmask>
        </address>
      </ipv4>
    </interface>
  </interfaces>
</config>
'''

# Connect and apply configuration
with manager.connect(**device, device_params={'name': 'iosxe'}) as m:
    print("Creating Loopback99 via NETCONF...")

    # Send edit-config RPC to modify running configuration
    # target='running' means apply directly to running-config
    # operation='merge' means merge with existing config (don't replace)
    netconf_reply = m.edit_config(
        target='running',
        config=config_loopback
    )

    # Check if operation succeeded
    if "<ok/>" in netconf_reply.xml:
        print("OK: Interface created successfully!")
        print("\nYou can verify with CLI command:")
        print("  show ip interface brief | include Loopback99")
    else:
        print("ERROR: Operation failed")
        print(netconf_reply)

Run the script:

python netconf_create_interface.py

Expected output:

Creating Loopback99 via NETCONF...
OK: Interface created successfully!

You can verify with CLI command:
  show ip interface brief | include Loopback99

4.2 Understanding the XML Payload

Let’s break down the XML configuration:

<config>                                                    <!-- Root element for edit-config -->
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">  <!-- YANG module namespace -->
    <interface>                                             <!-- Creates one interface -->
      <name>Loopback99</name>                              <!-- Interface name (key field) -->
      <description>Created via NETCONF</description>       <!-- Optional description -->
      <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">  <!-- Interface type namespace -->
        ianaift:softwareLoopback                          <!-- Loopback interface type -->
      </type>
      <enabled>true</enabled>                              <!-- no shutdown (up) -->
      <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">  <!-- IPv4 config (nested, different YANG module) -->
        <address>                                          <!-- IP address list -->
          <ip>192.0.2.99</ip>                             <!-- IP address -->
          <netmask>255.255.255.0</netmask>                <!-- Subnet mask -->
        </address>
      </ipv4>
    </interface>
  </interfaces>
</config>

Compare to Lab 3 RESTCONF JSON:

{
  "ietf-interfaces:interface": {
    "name": "Loopback99",
    "description": "Created via RESTCONF",
    "type": "iana-if-type:softwareLoopback",
    "enabled": true,
    "ietf-ip:ipv4": {
      "address": [
        {
          "ip": "192.0.2.99",
          "netmask": "255.255.255.0"
        }
      ]
    }
  }
}

Same data, different format! The YANG model defines the structure; NETCONF uses XML, RESTCONF uses JSON.

4.3 Verify the Change via CLI

On the router CLI:

Step 1: Check interface brief:

R1# show ip interface brief | include Loopback99

Expected output:

Loopback99             192.0.2.99      YES manual up                    up

Step 2: Check running configuration:

R1# show running-config interface Loopback99

Expected output:

interface Loopback99
 description Created via NETCONF
 ip address 192.0.2.99 255.255.255.0
end

Step 3: Verify detailed interface info:

R1# show ip interface Loopback99

You’ll see the IP address, subnet mask, and interface status - all matching what you configured via NETCONF!

Compare the three methods:

  • CLI: Type commands manually (Lab 1 knowledge)

  • RESTCONF: HTTP PUT with JSON (Lab 3)

  • NETCONF: edit-config RPC with XML (Lab 4)

All three create the exact same configuration on the device. The difference is how you interact with the device programmatically.

4.4 edit-config Operations

NETCONF supports different operations within edit-config:

Operation Behavior RESTCONF Equivalent

merge (default)

Add or update configuration elements

PUT/PATCH

replace

Replace entire configuration element

PUT

create

Create new element (error if exists)

PUT (409 conflict if exists)

delete

Remove configuration element

DELETE

remove

Remove element (no error if doesn’t exist)

DELETE

Example using operation="create" to ensure interface doesn’t already exist:

<config>
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="create">
      <name>Loopback100</name>
      <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type>
      <enabled>true</enabled>
    </interface>
  </interfaces>
</config>

If Loopback100 already exists, you’ll get an error: data-exists.

4.5 Practice Exercises

  1. Create Loopback100 with IP 10.99.100.1/24:

    • Modify the script to use different interface name and IP

    • Verify with show ip interface brief

  2. Create Loopback101 with description "Test Lab 4":

    • Include description field in XML

    • Verify with show running-config interface Loopback101

  3. Try to create Loopback99 again (should see error):

    • Use nc:operation="create" in XML

    • Observe the error response

    • This demonstrates NETCONF error handling

After each exercise, verify via CLI:

  • show ip interface brief

  • show running-config interface <name>

Section 5 - NETCONF edit-config: Modifying Existing Configuration

Now you’ll modify an existing interface using NETCONF, equivalent to RESTCONF PATCH in Lab 3 Section 4.

Comparison to Lab 3: In Lab 3, you used requests.patch() to modify only specific fields. With NETCONF edit-config using operation="merge", you achieve the same result: only specified fields are updated, others remain unchanged.

5.1 Modify Loopback99 Description

# netconf_modify_interface.py
# Modify an existing interface using NETCONF edit-config with merge

from ncclient import manager

device = {
    'host': '10.10.20.10',
    'port': 830,
    'username': 'admin',
    'password': 'Cisco123!',
    'hostkey_verify': False
}

# XML payload to modify only the description
# Using operation="merge" (default) to update specific fields
config_modify = '''
<config>
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface>
      <name>Loopback99</name>
      <description>Modified via NETCONF - Lab 4</description>
    </interface>
  </interfaces>
</config>
'''

with manager.connect(**device, device_params={'name': 'iosxe'}) as m:
    print("Modifying Loopback99 description via NETCONF...")

    # edit-config with merge operation (default)
    # This updates only the fields specified (description)
    # Other fields (IP address, enabled) remain unchanged
    netconf_reply = m.edit_config(
        target='running',
        config=config_modify
    )

    if "<ok/>" in netconf_reply.xml:
        print("OK: Interface description updated successfully!")
        print("\nVerify with CLI:")
        print("  show running-config interface Loopback99")
    else:
        print("ERROR: Operation failed")
        print(netconf_reply)

Run the script:

python netconf_modify_interface.py

5.2 Add Secondary IP Address

You can also add additional configuration elements:

# netconf_add_secondary_ip.py
# Add a secondary IP address to Loopback99

from ncclient import manager

device = {
    'host': '10.10.20.10',
    'port': 830,
    'username': 'admin',
    'password': 'Cisco123!',
    'hostkey_verify': False
}

# Add secondary IP address 192.0.2.100/24
# The merge operation adds to the existing address list
config_secondary = '''
<config>
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface>
      <name>Loopback99</name>
      <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
        <address>
          <ip>192.0.2.100</ip>
          <netmask>255.255.255.0</netmask>
        </address>
      </ipv4>
    </interface>
  </interfaces>
</config>
'''

with manager.connect(**device, device_params={'name': 'iosxe'}) as m:
    print("Adding secondary IP to Loopback99...")

    netconf_reply = m.edit_config(target='running', config=config_secondary)

    if "<ok/>" in netconf_reply.xml:
        print("OK: Secondary IP added successfully!")
        print("\nVerify with CLI:")
        print("  show running-config interface Loopback99")
        print("  show ip interface Loopback99 | include Secondary")
    else:
        print("ERROR: Operation failed")
        print(netconf_reply)

5.3 Verify the Change via CLI

On the router:

Step 1: Check running configuration:

R1# show running-config interface Loopback99

Expected output:

interface Loopback99
 description Modified via NETCONF - Lab 4
 ip address 192.0.2.99 255.255.255.0
 ip address 192.0.2.100 255.255.255.0 secondary
end

Step 2: Verify IP addresses:

R1# show ip interface brief | include Loopback99

Step 3: Check detailed interface info:

R1# show interfaces Loopback99

You should see:

  • Description: "Modified via NETCONF - Lab 4"

  • Primary IP: 192.0.2.99/24

  • Secondary IP: 192.0.2.100/24

Key observation: The merge operation added the secondary IP without removing the primary IP. This is equivalent to RESTCONF PATCH behavior.

5.4 Practice Exercises

  1. Change Loopback99 description again:

    • Update description to "Your Name - Lab 4"

    • Verify with CLI

  2. Disable Loopback100 (administratively down):

    • Set <enabled>false</enabled>

    • Verify with show ip interface brief (should show "administratively down")

  3. Re-enable Loopback100:

    • Set <enabled>true</enabled>

    • Verify interface comes back up

After each exercise, verify with CLI commands and observe that only the fields you specified in XML were changed.

Section 6 - NETCONF delete-config: Removing Configuration

Now you’ll delete an interface using NETCONF, equivalent to RESTCONF DELETE in Lab 3 Section 5.

Comparison to Lab 3: In Lab 3, you used requests.delete() with a URL pointing to the resource. Here, you’ll use edit-config with operation="delete" in the XML payload.

6.1 Delete Loopback99 Interface

# netconf_delete_interface.py
# Delete an interface using NETCONF edit-config with delete operation

from ncclient import manager

device = {
    'host': '10.10.20.10',
    'port': 830,
    'username': 'admin',
    'password': 'Cisco123!',
    'hostkey_verify': False
}

# XML payload with operation="delete" to remove the interface
# You only need to specify the interface name (key field)
config_delete = '''
<config>
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete">
      <name>Loopback99</name>
    </interface>
  </interfaces>
</config>
'''

with manager.connect(**device, device_params={'name': 'iosxe'}) as m:
    print("Deleting Loopback99 via NETCONF...")

    # edit-config with delete operation
    netconf_reply = m.edit_config(
        target='running',
        config=config_delete
    )

    if "<ok/>" in netconf_reply.xml:
        print("OK: Interface deleted successfully!")
        print("\nVerify with CLI:")
        print("  show ip interface brief | include Loopback99")
        print("  (should return no output)")
    else:
        print("ERROR: Operation failed")
        print(netconf_reply)

Run the script:

python netconf_delete_interface.py

6.2 Verify Deletion via CLI

On the router:

Step 1: Check interface brief (should be gone):

R1# show ip interface brief | include Loopback99

Expected output: (no output - interface no longer exists)

Step 2: Try to show interface config (should error):

R1# show running-config interface Loopback99

Expected output:

^
% Invalid input detected at '^' marker.

Step 3: Check all interfaces don’t include Loopback99:

R1# show ip interface brief

Loopback99 should not appear in the list.

Success! The interface was completely removed from the running configuration via NETCONF.

6.3 Delete vs Remove Operations

NETCONF provides two deletion operations:

Operation Behavior

delete

Remove element; error if element doesn’t exist

remove

Remove element; no error if element doesn’t exist (more forgiving)

Example using remove (safer):

<interface xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="remove">
  <name>Loopback99</name>
</interface>

If Loopback99 doesn’t exist, remove succeeds silently. With delete, you’d get an error.

6.4 Practice Exercises

  1. Delete Loopback100 and Loopback101:

    • Modify the script to delete both interfaces

    • You can do this in one edit-config with multiple <interface> elements

  2. Try to delete Loopback99 again:

    • Use operation="delete"

    • Observe the error: data-missing

    • Then try with operation="remove" and see that it succeeds without error

  3. Delete only the secondary IP from an interface (advanced):

    • Use delete operation on the specific <address> element within <ipv4>

    • This demonstrates granular deletion

After deletion, always verify with CLI commands to confirm the configuration is gone.

Section 7 - Working with Cisco 3650 Switch (VLANs Example)

Now you’ll work with the Cisco 3650 switch using NETCONF to create and manage VLANs, just like you did with RESTCONF in Lab 3 Section 6.

Comparison to Lab 3: In Lab 3, you used the Cisco-IOS-XE-vlan YANG model with RESTCONF. Here, you’ll use the same YANG model with NETCONF XML instead of JSON.

7.1 Understanding Cisco VLAN YANG Model

Cisco switches use a proprietary YANG model for VLANs:

The structure:

module Cisco-IOS-XE-vlan {
  container vlans {
    list vlan {
      key "id";
      leaf id;        // VLAN number (1-4094)
      leaf name;      // VLAN name
    }
  }
}

7.2 Create VLAN 100 on Switch

# netconf_create_vlan.py
# Create a VLAN on Cisco 3650 switch using NETCONF

from ncclient import manager

# Switch connection parameters
device = {
    'host': '10.10.20.30',  # SW1 IP address
    'port': 830,
    'username': 'admin',
    'password': 'Cisco123!',
    'hostkey_verify': False
}

# XML payload to create VLAN 100 named "Engineering"
# Uses Cisco-specific YANG model
config_vlan = '''
<config>
  <native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
    <vlan>
      <vlan-list xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-vlan">
        <id>100</id>
        <name>Engineering</name>
      </vlan-list>
    </vlan>
  </native>
</config>
'''

with manager.connect(**device, device_params={'name': 'iosxe'}) as m:
    print("Creating VLAN 100 on switch via NETCONF...")

    netconf_reply = m.edit_config(
        target='running',
        config=config_vlan
    )

    if "<ok/>" in netconf_reply.xml:
        print("OK: VLAN 100 created successfully!")
        print("\nVerify with CLI on switch:")
        print("  show vlan brief | include 100")
    else:
        print("ERROR: Operation failed")
        print(netconf_reply)

Run the script:

python netconf_create_vlan.py

7.3 Verify VLAN Creation via CLI

On the switch CLI:

SW1# show vlan brief | include 100

Expected output:

100  Engineering                      active

Full VLAN table:

SW1# show vlan brief

You should see VLAN 100 in the list with name "Engineering" and status "active".

7.4 Retrieve VLANs via NETCONF

# netconf_get_vlans.py
# Retrieve VLAN configuration from switch

from ncclient import manager
import xmltodict
import json

device = {
    'host': '10.10.20.30',
    'port': 830,
    'username': 'admin',
    'password': 'Cisco123!',
    'hostkey_verify': False
}

# Filter for VLAN configuration
filter_vlans = '''
<filter>
  <native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
    <vlan>
      <vlan-list xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-vlan"/>
    </vlan>
  </native>
</filter>
'''

with manager.connect(**device, device_params={'name': 'iosxe'}) as m:
    print("Retrieving VLANs from switch via NETCONF...\n")

    netconf_reply = m.get_config(source='running', filter=filter_vlans)

    # Parse XML to dictionary
    xml_dict = xmltodict.parse(netconf_reply.xml)

    # Navigate to VLAN list
    vlan_list = xml_dict['rpc-reply']['data']['native']['vlan']['vlan-list']

    # Print VLAN information
    print("VLANs configured on switch:")
    print("-" * 40)

    # Handle single VLAN (dict) vs multiple VLANs (list)
    if isinstance(vlan_list, list):
        for vlan in vlan_list:
            print(f"VLAN {vlan['id']}: {vlan.get('name', 'default')}")
    else:
        # Only one VLAN configured
        print(f"VLAN {vlan_list['id']}: {vlan_list.get('name', 'default')}")

Expected output:

Retrieving VLANs from switch via NETCONF...

VLANs configured on switch:

----------------------------------------
VLAN 1: default
VLAN 100: Engineering

Verify this matches CLI output:

SW1# show vlan brief

The NETCONF data matches the CLI output exactly.

7.5 Practice Exercises

  1. Create VLANs 200 and 300:

    • VLAN 200: "Sales"

    • VLAN 300: "Guest"

    • Verify with show vlan brief

  2. Modify VLAN 100 name to "Engineering-Dept":

    • Use edit-config with merge operation

    • Verify the name changed with CLI

  3. Delete VLAN 300:

    • Use edit-config with delete operation

    • Verify with show vlan brief

  4. Retrieve VLANs and print only VLAN IDs:

    • Modify the get script to extract only VLAN IDs

    • Print as a simple list

Common Errors & Troubleshooting

Error Possible Cause Solution

SSHException: Error reading SSH protocol banner

NETCONF not enabled on device

Enable with netconf-yang command

ncclient.transport.errors.AuthenticationError

Wrong username/password

Verify credentials match device config

socket.timeout

Network connectivity issue

Check IP address, ping device, verify port 830

<rpc-error> with data-exists

Interface/resource already exists

Use operation="merge" instead of "create", or delete first

<rpc-error> with data-missing

Trying to delete non-existent resource

Use operation="remove" instead of "delete"

<rpc-error> with invalid-value

XML payload doesn’t match YANG model

Verify XML structure matches YANG, check namespaces

XMLSyntaxError

Malformed XML in config string

Check for unclosed tags, missing namespaces

Config applied but doesn’t work

Wrong YANG model namespace

Verify namespace URIs match device capabilities

Interface created but no IP

Missing <ipv4> section in XML

Add complete <ipv4><address> structure

VLAN operations fail on routers

VLANs are switch-specific

Use switch IP address (10.10.20.30), not router

Debugging Tips

  1. Always check NETCONF is running: ` show platform software yang-management process `

  2. Print the full XML response to see errors: python print(netconf_reply.xml)

  3. Use get-config before edit-config:

    • Retrieve existing configuration structure

    • Use it as a template for your edit

  4. Verify device capabilities: python for cap in m.server_capabilities: if 'ietf-interfaces' in cap: print(cap)

  5. Test XML syntax separately: python import xml.etree.ElementTree as ET ET.fromstring(config_string) # Validates XML syntax

  6. Always verify via CLI:

    • Use show running-config, show ip interface brief, show vlan

    • Compare NETCONF data to CLI output

  7. Check ncclient version: bash pip show ncclient Ensure you have version 0.6.9 or later

Reflection Questions

After completing this lab activity you should be able to answer these questions:

  1. NETCONF vs RESTCONF: Based on your experience in Labs 3 and 4, which protocol do you prefer and why? Consider ease of use, error handling, and data format.

  2. Datastore Advantage: NETCONF supports candidate datastore (test changes before committing). Describe a real-world scenario where this would prevent a network outage that RESTCONF couldn’t prevent.

  3. XML vs JSON: Compare the XML payload you used to create Loopback99 in Lab 4 with the JSON payload from Lab 3. Which is more human-readable? Which is more verbose? Why do you think both formats exist?

  4. YANG Model Universality: Both NETCONF and RESTCONF use the same YANG models (ietf-interfaces, ietf-ip). Why is this important for network automation? How does it help when working with multiple devices?

  5. RPC vs REST: NETCONF uses RPC operations (<get-config>, <edit-config>). RESTCONF uses HTTP methods (GET, PUT). Which model makes more sense to you? Why?

  6. Error Handling: Compare the error messages you received in NETCONF vs RESTCONF when something went wrong. Which provided more useful information? Give specific examples.

  7. Namespace Complexity: NETCONF requires explicit XML namespaces (xmlns=). Did you find this helped clarify which YANG module was being used, or did it just add complexity?

  8. CLI Correlation: When you created Loopback99 via NETCONF, what CLI commands would create the same configuration? Show the mapping from XML elements to CLI commands.

  9. Vendor Support: Research question: Find two network device vendors (besides Cisco) that support NETCONF. Do they also support RESTCONF? What does this tell you about industry adoption?

  10. Transaction Support: NETCONF edit-config can include multiple changes that are committed as a unit. Design a scenario where you’d configure 3 related settings (e.g., OSPF area, network statement, router ID) that should succeed or fail together.

  11. Filter Complexity: You used subtree filtering in this lab. Research XPath filtering for NETCONF. Which would be better for retrieving "all interfaces with IP addresses in the 192.168.0.0/16 range"?

  12. Lab 5 Preparation: In Lab 5, you’ll compare SSH/Netmiko, RESTCONF, and NETCONF side-by-side. Based on Labs 1, 3, and 4, which method would you choose for:

    1. Configuring 100 identical switches

    2. Making one-time emergency configuration change

    3. Building network automation that must work with devices from multiple vendors

    4. Implementing configuration rollback capability

Optional Challenge Exercises

Challenge 1: Batch Interface Creation with NETCONF

Write a script that creates Loopback interfaces 100-110 using a loop. Use NETCONF edit-config. The script should:

  • Loop through interface numbers 100-110

  • Generate XML payload for each with IP 10.0.{id}.1/24

  • Send edit-config for each

  • Handle errors if an interface already exists

  • Print summary: "Created 11 interfaces successfully"

  • Verify with CLI: show ip interface brief | include Loopback

Hints:

  • Use Python f-strings to generate XML dynamically

  • Wrap each edit in try/except

  • Compare execution time to creating interfaces via CLI

Challenge 2: Configuration Audit Script

Write a script that audits all interfaces on the router for security compliance: 1. Use get-config to retrieve all interfaces 2. Check each interface for:

  • Has description (best practice)

  • If unused (no IP, admin down), should have specific description "UNUSED"

    1. Generate report showing non-compliant interfaces

    2. Bonus: For non-compliant interfaces, generate XML to fix them automatically

Challenge 3: Use Candidate Datastore

Rewrite one of your scripts to use candidate datastore workflow: 1. Lock the candidate datastore 2. Make changes to candidate 3. Validate candidate configuration 4. Commit to running (if valid) or discard (if invalid) 5. Unlock candidate datastore

This demonstrates NETCONF’s advanced transaction capability that RESTCONF doesn’t have.

Example:

with manager.connect(**device) as m:
    # Lock candidate
    m.lock(target='candidate')

    # Make changes to candidate
    m.edit_config(target='candidate', config=xml_config)

    # Validate
    m.validate(source='candidate')

    # Commit to running
    m.commit()

    # Unlock
    m.unlock(target='candidate')

Challenge 4: NETCONF vs RESTCONF Performance Test

Write two scripts that create 20 loopback interfaces: 1. One using NETCONF edit-config 2. One using RESTCONF PUT (from Lab 3)

Measure execution time for each. Which is faster? Why?

Consider:

  • Network overhead (HTTP headers vs SSH framing)

  • Connection setup time (HTTPS handshake vs SSH handshake)

  • Payload size (XML vs JSON)

Challenge 5: Parse YANG Model Capabilities

Write a script that: 1. Connects to device via NETCONF 2. Retrieves all capabilities 3. Extracts YANG module names and versions 4. Saves to CSV file: module_name, revision_date, namespace 5. Sorts by module name

This creates an inventory of available automation endpoints on the device.

Challenge 6: Cross-Platform NETCONF

If you have access to a non-Cisco device that supports NETCONF (Juniper, Arista, etc.): 1. Connect via NETCONF 2. Retrieve interface configuration 3. Compare the XML response structure to Cisco 4. Discuss similarities (ietf-interfaces) vs differences (vendor-specific)

Challenge 7: NETCONF Notification Subscription

Research NETCONF notifications (event streams). Write a script that: 1. Subscribes to a notification stream (e.g., interface state changes) 2. Listens for events 3. Prints notifications as they occur 4. Test by doing shut / no shut on an interface via CLI

This demonstrates NETCONF’s pub/sub capability for telemetry.

Other Automation Options: SNMP and gNMI

NETCONF and RESTCONF are not the only ways to automate and observe networks. Two common alternatives are SNMP and gNMI.

SNMP (Simple Network Management Protocol): A long-standing protocol focused on monitoring and basic configuration. It is widely supported and great for polling counters, but configuration workflows can be limited, difficult to understand, and vendor-specific.

gNMI (gRPC Network Management Interface): A modern, model-driven API that uses gRPC and YANG models. It is strong for streaming telemetry and structured data at scale, but device support varies by platform and it is a relatively new protocol with somewhat limited tooling and support.

Method Advantages Disadvantages

Netmiko (SSH CLI)

Easy to start, works on nearly any device with SSH, aligns with CLI knowledge

Unstructured text parsing, error handling is brittle, less safe for large scale changes

RESTCONF

Structured JSON, uses YANG models, standard HTTP tools and libraries

Model complexity, vendor differences in YANG support, HTTPS and authentication overhead

NETCONF

Transaction support, structured XML, strong error reporting, uses YANG models

XML verbosity, namespace complexity, client setup is heavier than REST

gNMI

Streaming telemetry, efficient at scale, model-driven with YANG

Platform support varies, requires gRPC tooling, fewer simple examples than REST/NETCONF

SNMP

Ubiquitous support, great for monitoring counters, lightweight polling

Limited configuration workflows, MIB complexity, security and version differences

What’s Next?

In Lab 5, you’ll combine everything you’ve learned:

  • SSH automation (Lab 1) - Direct CLI commands with Netmiko

  • RESTCONF (Lab 3) - REST API with JSON over HTTPS

  • NETCONF (Lab 4) - RPC operations with XML over SSH

You’ll configure a multi- device network (2 routers, 2 switches) and compare all three methods side-by-side for tasks like:

  • Deploying OSPF routing protocol

  • Creating VLANs across switches

  • Collecting operational data

  • Bulk configuration management

You’ll learn when to use each method based on:

  • Device support

  • Task requirements (transactions needed?)

  • Team skills

  • Integration with other systems

Lab 4 Complete! You now understand NETCONF and how it compares to RESTCONF. Both provide structured, model-driven automation using YANG. The choice between them depends on your specific requirements and device capabilities.