mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-29 20:23:01 +02:00
Merge pull request #1182 from kernelkit/doc2
doc: update scripting with restconf and add similar for netconf
This commit is contained in:
@@ -9,6 +9,8 @@ All notable changes to the project are documented in this file.
|
|||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
- Upgrade Linux kernel to 6.12.50 (LTS)
|
- Upgrade Linux kernel to 6.12.50 (LTS)
|
||||||
|
- Extend NETCONF and RESTCONF scripting documentation with operational
|
||||||
|
data examples, discovery patterns, and common workflow examples, issue #1156
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,738 @@
|
|||||||
|
# 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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ sudo apt install netopeer2-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
Connect to your Infix device:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ 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-config --source running
|
||||||
|
```
|
||||||
|
|
||||||
|
**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:**
|
||||||
|
|
||||||
|
```
|
||||||
|
> get --filter-xpath /interfaces
|
||||||
|
```
|
||||||
|
|
||||||
|
**Edit configuration:**
|
||||||
|
|
||||||
|
```
|
||||||
|
> edit-config --target candidate --config=/tmp/config.xml
|
||||||
|
> commit
|
||||||
|
```
|
||||||
|
|
||||||
|
**Disconnect:**
|
||||||
|
|
||||||
|
```
|
||||||
|
> disconnect
|
||||||
|
> quit
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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:**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<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:
|
||||||
|
|
||||||
|
```
|
||||||
|
> get --filter-xpath /interfaces/interface/name
|
||||||
|
```
|
||||||
|
|
||||||
|
This returns all interface names, useful for iterating through interfaces
|
||||||
|
in scripts.
|
||||||
|
|
||||||
|
### Get All YANG Capabilities
|
||||||
|
|
||||||
|
Discover which YANG modules and features are available:
|
||||||
|
|
||||||
|
```
|
||||||
|
> status
|
||||||
|
```
|
||||||
|
|
||||||
|
Or programmatically via the `<hello>` message capabilities received during
|
||||||
|
connection establishment.
|
||||||
|
|
||||||
|
### Get Entire Running Configuration
|
||||||
|
|
||||||
|
Useful for exploration or backup:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<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:
|
||||||
|
|
||||||
|
```
|
||||||
|
> get-config --source running
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Workflow Patterns
|
||||||
|
|
||||||
|
#### Pattern 1: Find interface by IP address
|
||||||
|
|
||||||
|
Get all interfaces with their IPs, then filter:
|
||||||
|
|
||||||
|
```
|
||||||
|
> get --filter-xpath /interfaces
|
||||||
|
```
|
||||||
|
|
||||||
|
Parse the XML output to find which interface has the desired IP.
|
||||||
|
|
||||||
|
#### Pattern 2: Check which interfaces are down
|
||||||
|
|
||||||
|
```
|
||||||
|
> get --filter-xpath /interfaces/interface/oper-status
|
||||||
|
```
|
||||||
|
|
||||||
|
Look for interfaces with `<oper-status>down</oper-status>`.
|
||||||
|
|
||||||
|
#### Pattern 3: Get interface statistics for monitoring
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<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](https://github.com/wires-se/netconf-client) is a lightweight
|
||||||
|
Python-based NETCONF client designed for scripting and automation.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ pip install netconf-client
|
||||||
|
```
|
||||||
|
|
||||||
|
Or clone and install from source:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ git clone https://github.com/wires-se/netconf-client.git
|
||||||
|
~$ cd netconf-client
|
||||||
|
~$ pip install .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
The client provides a simple command-line interface:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ netconf-client --host example.local --user admin --password admin <operation>
|
||||||
|
```
|
||||||
|
|
||||||
|
Common operations:
|
||||||
|
|
||||||
|
- `get-config` - Retrieve configuration
|
||||||
|
- `edit-config` - Modify configuration
|
||||||
|
- `get` - Retrieve operational state
|
||||||
|
- `copy-config` - Copy between datastores
|
||||||
|
- `lock/unlock` - Lock datastores
|
||||||
|
- `commit` - Commit candidate configuration
|
||||||
|
|
||||||
|
### Python API
|
||||||
|
|
||||||
|
You can also use netconf-client as a Python library:
|
||||||
|
|
||||||
|
```python
|
||||||
|
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:**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<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:**
|
||||||
|
|
||||||
|
```
|
||||||
|
> get-config --source running --filter-xpath /system/hostname
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using netconf-client:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ netconf-client --host example.local --user admin \
|
||||||
|
get-config --source running --xpath /system/hostname
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<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:**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<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`:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<config>
|
||||||
|
<system xmlns="urn:ietf:params:xml:ns:yang:ietf-system">
|
||||||
|
<hostname>newhostname</hostname>
|
||||||
|
</system>
|
||||||
|
</config>
|
||||||
|
```
|
||||||
|
|
||||||
|
Then apply:
|
||||||
|
|
||||||
|
```
|
||||||
|
> edit-config --target candidate --config=/tmp/hostname.xml
|
||||||
|
> commit
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using netconf-client:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ 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`:
|
||||||
|
|
||||||
|
```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:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ 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:**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<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:**
|
||||||
|
|
||||||
|
```
|
||||||
|
> copy-config --source running --target startup
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using netconf-client:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ netconf-client --host example.local --user admin \
|
||||||
|
copy-config --source running --target startup
|
||||||
|
```
|
||||||
|
|
||||||
|
## Operational Data Examples
|
||||||
|
|
||||||
|
### Read Interface State
|
||||||
|
|
||||||
|
**XML request:**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<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:**
|
||||||
|
|
||||||
|
```
|
||||||
|
> get --filter-xpath /interfaces/interface[name='eth0']
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using netconf-client:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ 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:**
|
||||||
|
|
||||||
|
```
|
||||||
|
> get --filter-xpath /interfaces
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using netconf-client:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ netconf-client --host example.local --user admin \
|
||||||
|
get --xpath /interfaces
|
||||||
|
```
|
||||||
|
|
||||||
|
### Read Routing Table
|
||||||
|
|
||||||
|
**Using netconf-client:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ netconf-client --host example.local --user admin \
|
||||||
|
get --xpath "/routing/ribs/rib[name='ipv4-default']"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Read OSPF Neighbors
|
||||||
|
|
||||||
|
**Using netconf-client:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ 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
|
||||||
|
|
||||||
|
```python
|
||||||
|
#!/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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ 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
|
||||||
|
|
||||||
|
```python
|
||||||
|
#!/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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ 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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ pip install ncclient
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
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:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- 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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ netconf-client --host example.local --user admin --verbose get-config
|
||||||
|
```
|
||||||
|
|
||||||
|
For netopeer2-cli, enable debug output:
|
||||||
|
|
||||||
|
```
|
||||||
|
> debug 1
|
||||||
|
> get-config --source running
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [NETCONF Protocol (RFC 6241)](https://datatracker.ietf.org/doc/html/rfc6241)
|
||||||
|
- [netconf-client on GitHub](https://github.com/wires-se/netconf-client)
|
||||||
|
- [netopeer2 Documentation](https://github.com/CESNET/netopeer2)
|
||||||
|
- [YANG Data Modeling Language (RFC 7950)](https://datatracker.ietf.org/doc/html/rfc7950)
|
||||||
|
- [ietf-interfaces YANG module](https://datatracker.ietf.org/doc/html/rfc8343)
|
||||||
|
- [ietf-system YANG module](https://datatracker.ietf.org/doc/html/rfc7317)
|
||||||
|
- [ncclient Documentation](https://ncclient.readthedocs.io/)
|
||||||
+369
-34
@@ -1,46 +1,149 @@
|
|||||||
# Examples using RESTCONF
|
# Scripting with RESTCONF
|
||||||
|
|
||||||
## Factory Reset
|
RESTCONF provides a programmatic interface to both configuration and
|
||||||
|
operational data over HTTPS. This guide shows practical examples using
|
||||||
|
`curl` to interact with the RESTCONF API.
|
||||||
|
|
||||||
```
|
All examples use the following conventions:
|
||||||
~$ curl -kX POST -u admin:admin \
|
|
||||||
-H "Content-Type: application/yang-data+json" \
|
- **Host**: `example.local` (replace with your device hostname/IP)
|
||||||
https://example.local/restconf/operations/ietf-factory-default:factory-reset
|
- **Credentials**: `admin:admin` (default username:password)
|
||||||
curl: (56) OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0
|
- **HTTPS**: Self-signed certificates require `-k` flag in curl
|
||||||
|
|
||||||
|
## Helper Script
|
||||||
|
|
||||||
|
To simplify RESTCONF operations, create a `curl.sh` wrapper script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
AUTH=${AUTH:-admin:admin}
|
||||||
|
HOST=${HOST:-infix.local}
|
||||||
|
|
||||||
|
method=$1
|
||||||
|
path=$2
|
||||||
|
shift 2
|
||||||
|
|
||||||
|
set -x
|
||||||
|
exec curl \
|
||||||
|
--insecure \
|
||||||
|
--user ${AUTH} \
|
||||||
|
--request ${method} \
|
||||||
|
--header "Content-Type: application/yang-data+json" \
|
||||||
|
--header "Accept: application/yang-data+json" \
|
||||||
|
"$@" \
|
||||||
|
https://${HOST}/restconf/ds/ietf-datastores:${path}
|
||||||
```
|
```
|
||||||
|
|
||||||
## System Reboot
|
Make it executable:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
~$ curl -kX POST -u admin:admin \
|
~$ chmod +x curl.sh
|
||||||
-H "Content-Type: application/yang-data+json" \
|
|
||||||
https://example.local/restconf/operations/ietf-system:system-restart
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Set Date and Time
|
This wrapper handles authentication, headers, and the base URL construction,
|
||||||
|
making commands much cleaner. You can override defaults with environment
|
||||||
|
variables:
|
||||||
|
|
||||||
Here's an example of an RPC that takes input/argument:
|
```bash
|
||||||
|
~$ HOST=192.168.1.10 AUTH=admin:secret ./curl.sh GET running/...
|
||||||
```
|
|
||||||
~$ curl -kX POST -u admin:admin \
|
|
||||||
-H "Content-Type: application/yang-data+json" \
|
|
||||||
-d '{"ietf-system:input": {"current-datetime": "2024-04-17T13:48:02-01:00"}}' \
|
|
||||||
https://example.local/restconf/operations/ietf-system:set-current-datetime
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can verify that the changes took by a remote SSH command:
|
The examples below show both raw `curl` commands and the equivalent using
|
||||||
|
`curl.sh` where applicable.
|
||||||
|
|
||||||
```
|
## Discovery & Common Patterns
|
||||||
~$ ssh admin@example.local 'date'
|
|
||||||
Wed Apr 17 14:48:12 UTC 2024
|
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:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces 2>/dev/null | jq -r '.["ietf-interfaces:interfaces"]["interface"][].name'
|
||||||
|
lo
|
||||||
|
e0
|
||||||
|
e1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Read Hostname
|
This is essential for automation - interface names vary by platform (eth0,
|
||||||
|
e1, enp0s3, etc.), so scripts should discover them rather than hardcode.
|
||||||
|
|
||||||
Example of fetching JSON configuration data to stdout:
|
### Get API Capabilities
|
||||||
|
|
||||||
|
Discover what YANG modules are available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ curl -kX GET -u admin:admin \
|
||||||
|
-H 'Accept: application/yang-data+json' \
|
||||||
|
https://example.local/restconf/data/ietf-yang-library:yang-library
|
||||||
|
```
|
||||||
|
|
||||||
|
This returns all supported YANG modules, revisions, and features.
|
||||||
|
|
||||||
|
### Get Entire Running Configuration
|
||||||
|
|
||||||
|
Useful for exploration or backup:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET running -o backup.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Workflow Patterns
|
||||||
|
|
||||||
|
#### Pattern 1: Find interface by IP address
|
||||||
|
|
||||||
|
Get all interfaces with IPs and search:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces 2>/dev/null \
|
||||||
|
| jq -r '.["ietf-interfaces:interfaces"]["interface"][] | select(.["ietf-ip:ipv4"]["address"][]?.ip == "192.168.1.100") | .name'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pattern 2: List all interfaces that are down
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces 2>/dev/null \
|
||||||
|
| jq -r '.["ietf-interfaces:interfaces"]["interface"][] | select(.["oper-status"] == "down") | .name'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pattern 3: Get statistics for all interfaces
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces 2>/dev/null \
|
||||||
|
| jq -r '.["ietf-interfaces:interfaces"]["interface"][] | "\(.name): RX \(.statistics["in-octets"]) TX \(.statistics["out-octets"])"'
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
lo: RX 29320 TX 29320
|
||||||
|
e0: RX 1847392 TX 892341
|
||||||
|
e1: RX 0 TX 0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pattern 4: Check if interface exists before configuring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ if ./curl.sh example.local GET running/ietf-interfaces:interfaces/interface=eth0 2>/dev/null | grep -q "ietf-interfaces:interface"; then
|
||||||
|
echo "Interface eth0 exists"
|
||||||
|
else
|
||||||
|
echo "Interface eth0 not found"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Operations
|
||||||
|
|
||||||
|
### Read Hostname
|
||||||
|
|
||||||
|
Example of fetching JSON configuration data:
|
||||||
|
|
||||||
|
**Using curl directly:**
|
||||||
|
|
||||||
|
```bash
|
||||||
~$ curl -kX GET -u admin:admin \
|
~$ curl -kX GET -u admin:admin \
|
||||||
-H 'Accept: application/yang-data+json' \
|
-H 'Accept: application/yang-data+json' \
|
||||||
https://example.local/restconf/data/ietf-system:system/hostname
|
https://example.local/restconf/data/ietf-system:system/hostname
|
||||||
@@ -51,27 +154,68 @@ Example of fetching JSON configuration data to stdout:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Set Hostname
|
**Using curl.sh:**
|
||||||
|
|
||||||
Example of inline JSON data:
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET running/ietf-system:system/hostname
|
||||||
|
{
|
||||||
|
"ietf-system:system": {
|
||||||
|
"hostname": "foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Set Hostname
|
||||||
|
|
||||||
|
Example of updating configuration with inline JSON data:
|
||||||
|
|
||||||
|
**Using curl directly:**
|
||||||
|
|
||||||
|
```bash
|
||||||
~$ curl -kX PATCH -u admin:admin \
|
~$ curl -kX PATCH -u admin:admin \
|
||||||
-H 'Content-Type: application/yang-data+json' \
|
-H 'Content-Type: application/yang-data+json' \
|
||||||
-d '{"ietf-system:system":{"hostname":"bar"}}' \
|
-d '{"ietf-system:system":{"hostname":"bar"}}' \
|
||||||
https://example.local/restconf/data/ietf-system:system
|
https://example.local/restconf/data/ietf-system:system
|
||||||
```
|
```
|
||||||
|
|
||||||
## Copy Running to Startup
|
**Using curl.sh:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local PATCH running/ietf-system:system \
|
||||||
|
-d '{"ietf-system:system":{"hostname":"bar"}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add IP Address to Interface
|
||||||
|
|
||||||
|
Add an IP address to the loopback interface:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local POST \
|
||||||
|
running/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254 \
|
||||||
|
-d '{ "prefix-length": 32 }'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete IP Address from Interface
|
||||||
|
|
||||||
|
Remove an IP address from the loopback interface:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local DELETE \
|
||||||
|
running/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254
|
||||||
|
```
|
||||||
|
|
||||||
|
### Copy Running to Startup
|
||||||
|
|
||||||
No copy command available yet to copy between datastores, and the
|
No copy command available yet to copy between datastores, and the
|
||||||
Rousette back-end also does not support "write-through" to the
|
Rousette back-end also does not support "write-through" to the
|
||||||
startup datastore.
|
startup datastore.
|
||||||
|
|
||||||
To save running-config to startup-config, use the following example to
|
To save running-config to startup-config, fetch running to a local file
|
||||||
fetch running to a local file and then update startup with it:
|
and then update startup with it:
|
||||||
|
|
||||||
```
|
**Using curl directly:**
|
||||||
|
|
||||||
|
```bash
|
||||||
~$ curl -kX GET -u admin:admin -o running-config.json \
|
~$ curl -kX GET -u admin:admin -o running-config.json \
|
||||||
-H 'Accept: application/yang-data+json' \
|
-H 'Accept: application/yang-data+json' \
|
||||||
https://example.local/restconf/ds/ietf-datastores:running
|
https://example.local/restconf/ds/ietf-datastores:running
|
||||||
@@ -80,3 +224,194 @@ fetch running to a local file and then update startup with it:
|
|||||||
-H 'Content-Type: application/yang-data+json' \
|
-H 'Content-Type: application/yang-data+json' \
|
||||||
https://example.local/restconf/ds/ietf-datastores:startup
|
https://example.local/restconf/ds/ietf-datastores:startup
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Using curl.sh:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET running -o running-config.json
|
||||||
|
~$ ./curl.sh example.local PUT startup -d @running-config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Operational Data
|
||||||
|
|
||||||
|
### Read Interface Configuration
|
||||||
|
|
||||||
|
Get the running configuration for the loopback interface:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET running/ietf-interfaces:interfaces/interface=lo
|
||||||
|
```
|
||||||
|
|
||||||
|
### Read Interface Operational State
|
||||||
|
|
||||||
|
Get operational data (state, statistics, etc.) for an interface:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces/interface=lo
|
||||||
|
```
|
||||||
|
|
||||||
|
This includes administrative and operational state, MAC address, MTU, and
|
||||||
|
statistics counters.
|
||||||
|
|
||||||
|
### Read Interface Statistics
|
||||||
|
|
||||||
|
Extract specific statistics using `jq`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces/interface=eth0 2>/dev/null \
|
||||||
|
| jq -r '.["ietf-interfaces:interfaces"]["interface"][0]["statistics"]["in-octets"]'
|
||||||
|
```
|
||||||
|
|
||||||
|
### List All Interfaces
|
||||||
|
|
||||||
|
Get operational data for all interfaces:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces
|
||||||
|
```
|
||||||
|
|
||||||
|
### Read Routing Table
|
||||||
|
|
||||||
|
Get the IPv4 routing table:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET operational/ietf-routing:routing/ribs/rib=ipv4-default
|
||||||
|
```
|
||||||
|
|
||||||
|
### Read OSPF State
|
||||||
|
|
||||||
|
Get OSPF operational data (neighbors, routes, etc.):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET operational/ietf-routing:routing/control-plane-protocols/control-plane-protocol=ietf-ospf:ospfv2,default
|
||||||
|
```
|
||||||
|
|
||||||
|
Or get just the neighbor information:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ./curl.sh example.local GET operational/ietf-routing:routing/control-plane-protocols/control-plane-protocol=ietf-ospf:ospfv2,default/ietf-ospf:ospf/areas/area=0.0.0.0/interfaces
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Operations (RPCs)
|
||||||
|
|
||||||
|
### Factory Reset
|
||||||
|
|
||||||
|
Reset the system to factory defaults:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ curl -kX POST -u admin:admin \
|
||||||
|
-H "Content-Type: application/yang-data+json" \
|
||||||
|
https://example.local/restconf/operations/ietf-factory-default:factory-reset
|
||||||
|
curl: (56) OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** The connection error is expected - the device resets immediately.
|
||||||
|
|
||||||
|
### System Reboot
|
||||||
|
|
||||||
|
Reboot the system:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ curl -kX POST -u admin:admin \
|
||||||
|
-H "Content-Type: application/yang-data+json" \
|
||||||
|
https://example.local/restconf/operations/ietf-system:system-restart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Set Date and Time
|
||||||
|
|
||||||
|
Example of an RPC that takes input/arguments:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ curl -kX POST -u admin:admin \
|
||||||
|
-H "Content-Type: application/yang-data+json" \
|
||||||
|
-d '{"ietf-system:input": {"current-datetime": "2024-04-17T13:48:02-01:00"}}' \
|
||||||
|
https://example.local/restconf/operations/ietf-system:set-current-datetime
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify the change with SSH:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ ssh admin@example.local 'date'
|
||||||
|
Wed Apr 17 14:48:12 UTC 2024
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Examples
|
||||||
|
|
||||||
|
### Makefile for Common Operations
|
||||||
|
|
||||||
|
Create a `Makefile` to simplify common operations:
|
||||||
|
|
||||||
|
```makefile
|
||||||
|
HOST ?= infix.local
|
||||||
|
|
||||||
|
lo-running:
|
||||||
|
./curl.sh $(HOST) GET running/ietf-interfaces:interfaces/interface=lo
|
||||||
|
|
||||||
|
lo-operational:
|
||||||
|
./curl.sh $(HOST) GET operational/ietf-interfaces:interfaces/interface=lo
|
||||||
|
|
||||||
|
lo-add-ip:
|
||||||
|
./curl.sh $(HOST) POST \
|
||||||
|
running/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254 \
|
||||||
|
-d '{ "prefix-length": 32 }'
|
||||||
|
|
||||||
|
lo-del-ip:
|
||||||
|
./curl.sh $(HOST) DELETE \
|
||||||
|
running/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254
|
||||||
|
|
||||||
|
%-stats:
|
||||||
|
@./curl.sh $(HOST) GET operational/ietf-interfaces:interfaces/interface=$* 2>/dev/null \
|
||||||
|
| jq -r '.["ietf-interfaces:interfaces"]["interface"][0]["statistics"]["in-octets"]'
|
||||||
|
|
||||||
|
%-monitor:
|
||||||
|
while sleep 0.2; do make -s HOST=$(HOST) $*-stats; done \
|
||||||
|
| ttyplot -t "$(HOST):$* in-octets" -r
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage examples:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get loopback operational state
|
||||||
|
~$ make lo-operational
|
||||||
|
|
||||||
|
# Add IP to loopback
|
||||||
|
~$ make lo-add-ip
|
||||||
|
|
||||||
|
# Get eth0 statistics
|
||||||
|
~$ make eth0-stats
|
||||||
|
|
||||||
|
# Monitor eth0 traffic in real-time (requires ttyplot)
|
||||||
|
~$ make eth0-monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
You can override the host:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ make HOST=192.168.1.10 lo-operational
|
||||||
|
```
|
||||||
|
|
||||||
|
### Monitoring Interface Traffic
|
||||||
|
|
||||||
|
The `%-monitor` target demonstrates real-time monitoring by polling
|
||||||
|
interface statistics and piping to `ttyplot` for visualization. Install
|
||||||
|
`ttyplot` with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ sudo apt install ttyplot
|
||||||
|
```
|
||||||
|
|
||||||
|
Then monitor any interface:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~$ make eth0-monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a live ASCII graph of incoming octets on `eth0`.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [RESTCONF Protocol (RFC 8040)](https://datatracker.ietf.org/doc/html/rfc8040)
|
||||||
|
- [YANG Data Modeling Language (RFC 7950)](https://datatracker.ietf.org/doc/html/rfc7950)
|
||||||
|
- [ietf-interfaces YANG module](https://datatracker.ietf.org/doc/html/rfc8343)
|
||||||
|
- [ietf-routing YANG module](https://datatracker.ietf.org/doc/html/rfc8349)
|
||||||
|
- [ietf-system YANG module](https://datatracker.ietf.org/doc/html/rfc7317)
|
||||||
|
|||||||
+2
-1
@@ -47,7 +47,8 @@ nav:
|
|||||||
- Introduction: scripting.md
|
- Introduction: scripting.md
|
||||||
- Legacy Scripting: scripting-sysrepocfg.md
|
- Legacy Scripting: scripting-sysrepocfg.md
|
||||||
- Production Testing: scripting-prod.md
|
- Production Testing: scripting-prod.md
|
||||||
- Remote RESTCONF: scripting-restconf.md
|
- With NETCONF: scripting-netconf.md
|
||||||
|
- With RESTCONF: scripting-restconf.md
|
||||||
- Developer's Corner:
|
- Developer's Corner:
|
||||||
- Branding & Releases: branding.md
|
- Branding & Releases: branding.md
|
||||||
- Developer's Guide: developers-guide.md
|
- Developer's Guide: developers-guide.md
|
||||||
|
|||||||
Reference in New Issue
Block a user