Scripting with NETCONF¶
NETCONF (Network Configuration Protocol) provides a standardized mechanism for managing network devices using XML-based RPC operations over SSH (port 830). This guide shows practical examples for interacting with Infix using NETCONF.
NETCONF offers robust capabilities for network automation:
- Transactional operations: Validate before commit, rollback on error
- Fine-grained locking: Prevent concurrent configuration conflicts
- Structured data: XML with YANG schema validation
- Standardized operations: Get, edit-config, copy-config, etc.
NETCONF vs RESTCONF¶
Both protocols use the same YANG data models, but differ in approach:
Feature | NETCONF | RESTCONF |
---|---|---|
Transport | SSH | HTTPS |
Encoding | XML | JSON/XML |
Operations | RPC-based | REST/HTTP methods |
Best for | Automation scripts | Web integration, simple queries |
Choose NETCONF when you need:
- Transactional configuration changes
- Configuration validation before commit
- Locking to prevent concurrent changes
- Integration with existing NETCONF tooling
Choose RESTCONF for:
- Simple queries and updates
- Web-based applications
- When you prefer JSON over XML
- RESTful API patterns
Quick Start with netopeer2-cli¶
netopeer2-cli
is an interactive NETCONF client, useful for learning and
testing. Install it:
Connect to your Infix device:
~$ netopeer2-cli
> connect --host example.local --login admin
admin@example.local password:
> status
Current NETCONF session:
ID : 1
Host : example.local
Port : 830
Transport : SSH
Capabilities: 35
Basic Operations in netopeer2-cli¶
Get entire configuration:
Get specific subtree (hostname):
> get-config --source running --filter-xpath /system/hostname
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<system xmlns="urn:ietf:params:xml:ns:yang:ietf-system">
<hostname>example</hostname>
</system>
</data>
Get operational state:
Edit configuration:
Disconnect:
Discovery & Common Patterns¶
Before working with specific configuration items, you often need to discover what exists on the system. This section shows common discovery patterns and practical workflows.
Discovering Available Interfaces¶
List all interface names:
<rpc message-id="1" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name/>
</interface>
</interfaces>
</filter>
</get>
</rpc>
Using netopeer2-cli:
This returns all interface names, useful for iterating through interfaces in scripts.
Get All YANG Capabilities¶
Discover which YANG modules and features are available:
Or programmatically via the <hello>
message capabilities received during
connection establishment.
Get Entire Running Configuration¶
Useful for exploration or backup:
<rpc message-id="2" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get-config>
<source>
<running/>
</source>
</get-config>
</rpc>
Using netopeer2-cli:
Common Workflow Patterns¶
Pattern 1: Find interface by IP address¶
Get all interfaces with their IPs, then filter:
Parse the XML output to find which interface has the desired IP.
Pattern 2: Check which interfaces are down¶
Look for interfaces with <oper-status>down</oper-status>
.
Pattern 3: Get interface statistics for monitoring¶
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<statistics/>
</interface>
</interfaces>
</filter>
</get>
</rpc>
Returns in-octets, out-octets, in-errors, out-errors for all interfaces.
Scripting with netconf-client¶
netconf-client is a lightweight Python-based NETCONF client designed for scripting and automation.
Installation¶
Or clone and install from source:
Basic Usage¶
The client provides a simple command-line interface:
Common operations:
get-config
- Retrieve configurationedit-config
- Modify configurationget
- Retrieve operational statecopy-config
- Copy between datastoreslock/unlock
- Lock datastorescommit
- Commit candidate configuration
Python API¶
You can also use netconf-client as a Python library:
from netconf_client.client import NetconfClient
# Connect
client = NetconfClient(
host='example.local',
username='admin',
password='admin'
)
# Get configuration
config = client.get_config(source='running')
print(config)
# Edit configuration
xml_config = """
<config>
<system xmlns="urn:ietf:params:xml:ns:yang:ietf-system">
<hostname>newhostname</hostname>
</system>
</config>
"""
client.edit_config(target='candidate', config=xml_config)
client.commit()
# Close connection
client.close()
Configuration Examples¶
Read Hostname¶
XML request:
<rpc message-id="1" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get-config>
<source>
<running/>
</source>
<filter type="subtree">
<system xmlns="urn:ietf:params:xml:ns:yang:ietf-system">
<hostname/>
</system>
</filter>
</get-config>
</rpc>
Using netopeer2-cli:
Using netconf-client:
~$ netconf-client --host example.local --user admin \
get-config --source running --xpath /system/hostname
Response:
<rpc-reply message-id="1" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<data>
<system xmlns="urn:ietf:params:xml:ns:yang:ietf-system">
<hostname>example</hostname>
</system>
</data>
</rpc-reply>
Set Hostname¶
XML request:
<rpc message-id="2" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
<candidate/>
</target>
<config>
<system xmlns="urn:ietf:params:xml:ns:yang:ietf-system">
<hostname>newhostname</hostname>
</system>
</config>
</edit-config>
</rpc>
Using netopeer2-cli:
Save the config to /tmp/hostname.xml
:
<config>
<system xmlns="urn:ietf:params:xml:ns:yang:ietf-system">
<hostname>newhostname</hostname>
</system>
</config>
Then apply:
Using netconf-client:
~$ cat > hostname.xml <<EOF
<config>
<system xmlns="urn:ietf:params:xml:ns:yang:ietf-system">
<hostname>newhostname</hostname>
</system>
</config>
EOF
~$ netconf-client --host example.local --user admin \
edit-config --target candidate --config hostname.xml
~$ netconf-client --host example.local --user admin commit
Add IP Address to Interface¶
Save the config to ip-config.xml
:
<config>
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>eth0</name>
<ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<address>
<ip>192.168.1.100</ip>
<prefix-length>24</prefix-length>
</address>
</ipv4>
</interface>
</interfaces>
</config>
Using netconf-client:
~$ netconf-client --host example.local --user admin \
edit-config --target candidate --config ip-config.xml
~$ netconf-client --host example.local --user admin commit
Copy Running to Startup¶
XML request:
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<copy-config>
<source>
<running/>
</source>
<target>
<startup/>
</target>
</copy-config>
</rpc>
Using netopeer2-cli:
Using netconf-client:
Operational Data Examples¶
Read Interface State¶
XML request:
<rpc message-id="4" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>eth0</name>
</interface>
</interfaces>
</filter>
</get>
</rpc>
Using netopeer2-cli:
Using netconf-client:
~$ netconf-client --host example.local --user admin \
get --xpath "/interfaces/interface[name='eth0']"
This returns operational state including admin/oper status, statistics, MAC address, MTU, and IP addresses.
Read All Interfaces¶
Using netopeer2-cli:
Using netconf-client:
Read Routing Table¶
Using netconf-client:
~$ netconf-client --host example.local --user admin \
get --xpath "/routing/ribs/rib[name='ipv4-default']"
Read OSPF Neighbors¶
Using netconf-client:
~$ netconf-client --host example.local --user admin \
get --xpath "/routing/control-plane-protocols/control-plane-protocol[type='ietf-ospf:ospfv2'][name='default']/ietf-ospf:ospf"
Advanced Scripting¶
Python Script: Backup Configuration¶
#!/usr/bin/env python3
from netconf_client.client import NetconfClient
from datetime import datetime
import sys
def backup_config(host, user, password, output_file):
try:
# Connect
client = NetconfClient(host=host, username=user, password=password)
# Get running config
config = client.get_config(source='running')
# Save to file with timestamp
timestamp = datetime.now().strftime('%Y%m%d-%H%M%S')
filename = f"{output_file}-{timestamp}.xml"
with open(filename, 'w') as f:
f.write(config)
print(f"Configuration backed up to {filename}")
client.close()
return 0
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
return 1
if __name__ == '__main__':
if len(sys.argv) != 5:
print(f"Usage: {sys.argv[0]} <host> <user> <password> <output_prefix>")
sys.exit(1)
sys.exit(backup_config(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]))
Usage:
~$ chmod +x backup.py
~$ ./backup.py example.local admin admin config-backup
Configuration backed up to config-backup-20250102-143022.xml
Python Script: Monitor Interface Statistics¶
#!/usr/bin/env python3
from netconf_client.client import NetconfClient
import xml.etree.ElementTree as ET
import time
import sys
def get_interface_stats(client, interface):
"""Get interface statistics"""
xpath = f"/interfaces/interface[name='{interface}']/statistics"
data = client.get(filter_xpath=xpath)
# Parse XML to extract counters
root = ET.fromstring(data)
ns = {'if': 'urn:ietf:params:xml:ns:yang:ietf-interfaces'}
stats = {}
for stat in root.findall('.//if:statistics/*', ns):
stats[stat.tag.split('}')[1]] = int(stat.text)
return stats
def monitor_interface(host, user, password, interface, interval=5):
"""Monitor interface statistics"""
client = NetconfClient(host=host, username=user, password=password)
print(f"Monitoring {interface} on {host} (Ctrl-C to stop)")
print(f"{'Time':<20} {'RX Packets':<15} {'TX Packets':<15} {'RX Bytes':<15} {'TX Bytes':<15}")
print("-" * 80)
try:
while True:
stats = get_interface_stats(client, interface)
timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
print(f"{timestamp:<20} "
f"{stats.get('in-unicast-pkts', 0):<15} "
f"{stats.get('out-unicast-pkts', 0):<15} "
f"{stats.get('in-octets', 0):<15} "
f"{stats.get('out-octets', 0):<15}")
time.sleep(interval)
except KeyboardInterrupt:
print("\nMonitoring stopped")
finally:
client.close()
if __name__ == '__main__':
if len(sys.argv) < 5:
print(f"Usage: {sys.argv[0]} <host> <user> <password> <interface> [interval]")
sys.exit(1)
interval = int(sys.argv[5]) if len(sys.argv) > 5 else 5
monitor_interface(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], interval)
Usage:
~$ chmod +x monitor.py
~$ ./monitor.py example.local admin admin eth0 2
Monitoring eth0 on example.local (Ctrl-C to stop)
Time RX Packets TX Packets RX Bytes TX Bytes
--------------------------------------------------------------------------------
2025-01-02 14:35:10 12453 8932 1847392 892341
2025-01-02 14:35:12 12489 8967 1851204 895673
...
Shell Script: Batch Configuration¶
#!/bin/bash
# Apply configuration to multiple devices
DEVICES="device1.local device2.local device3.local"
USER="admin"
PASSWORD="admin"
CONFIG_FILE="$1"
if [ -z "$CONFIG_FILE" ]; then
echo "Usage: $0 <config.xml>"
exit 1
fi
for device in $DEVICES; do
echo "Configuring $device..."
# Edit candidate
netconf-client --host "$device" --user "$USER" --password "$PASSWORD" \
edit-config --target candidate --config "$CONFIG_FILE"
if [ $? -eq 0 ]; then
# Commit if edit succeeded
netconf-client --host "$device" --user "$USER" --password "$PASSWORD" commit
echo " ✓ $device configured successfully"
else
echo " ✗ $device configuration failed"
fi
done
Other NETCONF Tools¶
ncclient (Python)¶
Popular Python library for NETCONF:
Example:
from ncclient import manager
with manager.connect(host='example.local', port=830,
username='admin', password='admin',
hostkey_verify=False) as m:
# Get config
c = m.get_config(source='running')
print(c)
Ansible¶
Ansible includes NETCONF modules for automation:
- name: Get interface config
netconf_get:
source: running
filter: <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>
Cisco Tools¶
- NSO (Network Services Orchestrator): Enterprise orchestration platform
- YANG Suite: Web-based YANG exploration and testing tool
- Cisco pyATS: Network test automation
Troubleshooting¶
Enable NETCONF Debugging¶
For netconf-client, use verbose mode:
For netopeer2-cli, enable debug output:
Common Issues¶
Connection refused:
- Verify SSH is running on port 830:
ssh -p 830 admin@example.local
- Check firewall rules
Authentication failed:
- Verify credentials
- Check user has NETCONF access permissions
Operation not supported:
- Verify NETCONF capability:
netopeer2-cli
→status
→ check capabilities - Some operations require specific YANG modules