c686fac55e951cd130db2e687c7af904fe60979e
get_vlans() relied on `bridge vlan show` for port membership. On devices whose BusyBox ships without the bridge/ip-full packages the command does not exist, _send_command returns the shell error, and the parser silently finds nothing in it. All that survived was the sub-interface fallback, which restates the bridge topology (br-ap tagged, br-ap.10 untagged) and never names the uplink port — the only port whose tagging matters. Measured on two Sophos AP100 (BusyBox 1.37.0): eth0 was absent from the output entirely, while UCI held ports='eth0:u*' for the management VLAN and 'eth0:t' for the rest. UCI bridge-vlan sections are now parsed as a second source. They are readable without the bridge binary and describe the configured state. Section collection goes through the type declaration, so named sections (network.apbr_vlan10) are recognised alongside anonymous ones — the old regex matched only @bridge-vlan[N], so a hand-built bridge was invisible even for name lookup. Runtime data keeps precedence where it exists, since that is what the kernel actually enforces. Option values are kept raw through collection; stripping quotes there would collapse ports='lan1:t' 'lan2:t' into a single mangled item. Also repairs TestGetVlans, red on master since get_vlans() moved to separate tagged/untagged lists while the tests still asserted the old 'interfaces' key, and TestGetFacts, which never learned about the number_of_interfaces key get_facts() sets deliberately. Both had left the interesting behaviour uncovered.
napalm-openwrt
NAPALM community driver for OpenWrt routers and access-points.
Communicates over SSH using Netmiko (linux device type).
Requires OpenWrt 19.07 or newer.
Tested devices
| Model | OpenWrt version | Tested |
|---|---|---|
| TP-Link TL-WR1043N/ND v5 | 23.05.3 | ✅ |
Contributions for additional devices and firmware versions are welcome.
Requirements
| Dependency | Minimum version |
|---|---|
| Python | 3.8 |
| NAPALM | 4.0 |
| Netmiko | 4.0 |
Installation
From source:
git clone https://github.com/napalm-automation-community/napalm-openwrt
cd napalm-openwrt
pip install -e .
Quick start
from napalm import get_network_driver
driver = get_network_driver("openwrt")
device = driver("192.168.1.1", "root", "")
device.open()
facts = device.get_facts()
print(facts)
interfaces = device.get_interfaces()
print(interfaces)
device.close()
Supported NAPALM getters
| Getter | Supported | Notes |
|---|---|---|
get_facts |
✅ | Uses /etc/openwrt_release, /tmp/sysinfo/model, /proc/uptime |
get_interfaces |
✅ | Uses ip link show |
get_interfaces_ip |
✅ | Uses ip addr show |
get_interfaces_counters |
✅ | Uses /proc/net/dev |
get_arp_table |
✅ | Uses ip neigh show |
get_mac_address_table |
✅ | Uses bridge fdb show |
get_config |
✅ | Uses uci export |
get_environment |
✅ | CPU from /proc/stat, memory from /proc/meminfo |
get_lldp_neighbors |
✅ | Requires lldpd package installed on device |
get_lldp_neighbors_detail |
✅ | Requires lldpd package installed on device |
Configuration management
Configuration is managed via UCI (Unified Configuration Interface).
Merge candidate
device.load_merge_candidate(config="""
uci set system.@system[0].hostname='my-router'
uci set network.lan.ipaddr='10.0.0.1'
""")
print(device.compare_config())
device.commit_config()
Replace candidate
with open("full-config.uci") as f:
device.load_replace_candidate(config=f.read())
print(device.compare_config())
device.commit_config()
Rollback
# Reverts to the config state before the last commit_config call
device.rollback()
Development
# Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Lint
ruff check napalm_openwrt/
Languages
Python
100%