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
requestslibrary and REST API concepts (from Labs 2-3)
New skills you’ll learn:
-
The
ncclientPython 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
ncclientlibrary 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:
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, R210.10.20.20/24on GigabitEthernet0/0/1 -
Cisco 3650 Switches - SW1
10.10.20.30/24, SW210.10.20.40/24on VLAN 1
All devices run Cisco IOS-XE and start with no configuration. You will:
-
Console into each device
-
Configure management interfaces and IP addresses
-
Enable NETCONF (instead of RESTCONF)
-
Create user accounts for API access
-
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 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:
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:
|
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?
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. |
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 ( |
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 |
Typical workflow with candidate datastore:
-
Lock candidate datastore
-
Edit candidate configuration (make changes)
-
Validate candidate configuration
-
Commit candidate to running (or discard if invalid)
-
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 (operational data) |
Retrieve operational state (live data) |
running |
|
GET (config data) |
Retrieve configuration |
running/candidate/startup |
|
PUT/PATCH/POST |
Modify configuration |
running/candidate |
|
DELETE |
Remove configuration |
startup/candidate |
|
(none) |
Copy entire config between datastores |
any |
|
(none) |
Lock datastore for exclusive access |
any |
|
(none) |
Commit candidate to running |
candidate → running |
|
(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-configoredit-configwith 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 |
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:
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/1with IP10.10.20.10andenabled: true -
CLI shows the same interface as
upwith IP10.10.20.10 -
NETCONF
enabled: false= CLIadministratively down
The data is identical, proving NETCONF retrieves the actual device configuration.
3.4 Practice Exercises
Modify the scripts to:
-
Retrieve all interfaces and print only interface names:
-
Extract
interface_data['name']for each interface -
Print in a simple list format
-
-
Count how many interfaces are enabled vs. disabled:
-
Loop through interfaces
-
Check
enabledfield (true/false) -
Print summary: "5 enabled, 3 disabled"
-
-
Find which interface has IP 10.10.20.10:
-
Loop through interfaces
-
Check if
ipv4key 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 |
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:
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 |
|---|---|---|
|
Add or update configuration elements |
PUT/PATCH |
|
Replace entire configuration element |
PUT |
|
Create new element (error if exists) |
PUT (409 conflict if exists) |
|
Remove configuration element |
DELETE |
|
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
-
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
-
-
Create Loopback101 with description "Test Lab 4":
-
Include description field in XML
-
Verify with
show running-config interface Loopback101
-
-
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 |
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
-
Change Loopback99 description again:
-
Update description to "Your Name - Lab 4"
-
Verify with CLI
-
-
Disable Loopback100 (administratively down):
-
Set
<enabled>false</enabled> -
Verify with
show ip interface brief(should show "administratively down")
-
-
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 |
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 |
|---|---|
|
Remove element; error if element doesn’t exist |
|
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
-
Delete Loopback100 and Loopback101:
-
Modify the script to delete both interfaces
-
You can do this in one edit-config with multiple
<interface>elements
-
-
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
-
-
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 |
7.1 Understanding Cisco VLAN YANG Model
Cisco switches use a proprietary YANG model for VLANs:
-
Module:
Cisco-IOS-XE-vlan -
Namespace:
http://cisco.com/ns/yang/Cisco-IOS-XE-vlan
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
-
Create VLANs 200 and 300:
-
VLAN 200: "Sales"
-
VLAN 300: "Guest"
-
Verify with
show vlan brief
-
-
Modify VLAN 100 name to "Engineering-Dept":
-
Use edit-config with merge operation
-
Verify the name changed with CLI
-
-
Delete VLAN 300:
-
Use edit-config with delete operation
-
Verify with
show vlan brief
-
-
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 |
|---|---|---|
|
NETCONF not enabled on device |
Enable with |
|
Wrong username/password |
Verify credentials match device config |
|
Network connectivity issue |
Check IP address, ping device, verify port 830 |
|
Interface/resource already exists |
Use operation="merge" instead of "create", or delete first |
|
Trying to delete non-existent resource |
Use operation="remove" instead of "delete" |
|
XML payload doesn’t match YANG model |
Verify XML structure matches YANG, check namespaces |
|
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 |
Add complete |
VLAN operations fail on routers |
VLANs are switch-specific |
Use switch IP address (10.10.20.30), not router |
Debugging Tips
-
Always check NETCONF is running:
` show platform software yang-management process` -
Print the full XML response to see errors:
python print(netconf_reply.xml) -
Use get-config before edit-config:
-
Retrieve existing configuration structure
-
Use it as a template for your edit
-
-
Verify device capabilities:
python for cap in m.server_capabilities: if 'ietf-interfaces' in cap: print(cap) -
Test XML syntax separately:
python import xml.etree.ElementTree as ET ET.fromstring(config_string) # Validates XML syntax -
Always verify via CLI:
-
Use
show running-config,show ip interface brief,show vlan -
Compare NETCONF data to CLI output
-
-
Check ncclient version:
Ensure you have version 0.6.9 or laterbash pip show ncclient
Reflection Questions
After completing this lab activity you should be able to answer these questions:
-
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.
-
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.
-
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?
-
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?
-
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?
-
Error Handling: Compare the error messages you received in NETCONF vs RESTCONF when something went wrong. Which provided more useful information? Give specific examples.
-
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?
-
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.
-
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?
-
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.
-
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"?
-
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:
-
Configuring 100 identical switches
-
Making one-time emergency configuration change
-
Building network automation that must work with devices from multiple vendors
-
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"
-
Generate report showing non-compliant interfaces
-
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.
Additional Resources
-
NETCONF RFC 6241: https://datatracker.ietf.org/doc/html/rfc6241
-
ncclient Documentation: https://ncclient.readthedocs.io/
-
Cisco NETCONF Guide: https://developer.cisco.com/docs/ios-xe/#!netconf
-
YANG Models on GitHub: https://github.com/YangModels/yang
-
Comparing RESTCONF and NETCONF: https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/prog/configuration/1612/b_1612_programmability_cg/restconf_programmable_interface.html
-
XML vs JSON Comparison: https://www.w3schools.com/js/js_json_xml.asp
-
NETCONF Tutorial: https://ultraconfig.com.au/blog/learn-netconf-by-doing-with-cisco-ios-xe/
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.