abce85d6efeb9dfd9bac0c6c83f91d61b3f4496a
_resolve_node() took the first entry of GET /nodes. In a cluster that lists every member, so a node polled without an explicit `node` driver argument talked to whichever member came first: pve-dual reported pve-02's name and VMs, and netOrk's VM sync moved pve-02's VM devices over to it. Resolve through GET /cluster/status instead: the entry marked local, then a match by IP or (short) name, then the sole node of a standalone host, and otherwise raise rather than guess. The lookup no longer swallows API errors either. A TLS verification failure used to leave the IP as the node name, so open() succeeded and every getter failed quietly while the poll reported success with empty data. It now surfaces as a ConnectionException from open(). Refs NetOrk/netork#417, NetOrk/netork#418
napalm-proxmox
A NAPALM driver for Proxmox VE nodes.
It supports all three networking domains of Proxmox VE:
| Domain | Description |
|---|---|
| Classic Linux | /etc/network/interfaces, Linux bridges, VLANs |
| SDN | Zones (VLAN, QinQ, VXLAN, EVPN), VNets, subnets |
| OVS | Open vSwitch bridges, bonds, and internal ports |
Requirements
- Python ≥ 3.9
- NAPALM ≥ 5.0.0
- proxmoxer ≥ 2.0.0
- netaddr ≥ 0.9.0
- requests ≥ 2.31.0
Installation
pip install napalm-proxmox
Or directly from source:
git clone https://github.com/example/napalm-proxmox.git
cd napalm-proxmox
pip install -e .
Usage
Password authentication
from napalm import get_network_driver
driver = get_network_driver("proxmox")
device = driver(
hostname="pve1.example.com",
username="root",
password="secret",
optional_args={
"realm": "pam", # default: "pam"
"port": 8006, # default: 8006
"verify_ssl": True, # default: True
},
)
with device:
facts = device.get_facts()
print(facts)
# {'vendor': 'Proxmox Server Solutions GmbH', 'model': 'PowerEdge R640',
# 'hostname': 'pve1', 'os_version': 'Proxmox VE 8.2.4', ...}
interfaces = device.get_interfaces()
interfaces_ip = device.get_interfaces_ip()
vlans = device.get_vlans()
network_instances = device.get_network_instances()
API token authentication
device = driver(
hostname="pve1.example.com",
username="napalm",
password="",
optional_args={
"token_name": "napalm@pam!napalm-token",
"token_value": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
},
)
Targeting a specific node
In a multi-node cluster the driver auto-detects the node by matching the hostname against the cluster's node list. You can also pin a specific node:
device = driver(
hostname="pve-cluster.example.com", # cluster VIP
username="root",
password="secret",
optional_args={"node": "pve2"},
)
Supported NAPALM methods
| Method | Notes |
|---|---|
open / close |
Password + API token auth |
is_alive |
Checks /api2/json/version |
get_facts |
vendor, model, hostname, os_version, uptime, interface_list, fqdn |
get_interfaces |
eth, bridge, OVS bridge/bond; speed, MTU, MAC, status |
get_interfaces_ip |
IPv4/IPv6 from classic ifaces + SDN subnet gateways |
get_interfaces_counters |
From node/netstat RRD data |
get_environment |
CPU %, memory (bytes), hardware temperature sensors |
get_arp_table |
Reads /proc/net/arp via exec endpoint |
get_mac_address_table |
bridge fdb show (Linux) + ovs-appctl fdb/show (OVS) |
get_vlans |
SDN VNet tags + bridge vlan show |
get_network_instances |
SDN zones (VLAN→L2VPN, EVPN→L3VRF) + default instance |
get_ntp_servers |
From node/ntp API |
get_ntp_stats |
Parses chronyc / ntpq -pn output |
get_snmp_information |
Parses /etc/snmp/snmpd.conf |
get_users |
Proxmox access/users API + local /etc/passwd |
get_config |
/etc/network/interfaces + SDN config; sanitize support |
load_merge_candidate / load_replace_candidate |
String or file |
compare_config |
Unified diff |
commit_config |
Writes /etc/network/interfaces + ifreload -a |
discard_config / rollback |
Revert candidate |
get_lldp_neighbors |
lldpcli show neighbors via exec |
get_lldp_neighbors_detail |
Full LLDP detail |
get_ipv6_neighbors_table |
ip -6 neigh show |
ping |
Linux ping via exec endpoint |
traceroute |
traceroute -n via exec endpoint |
cli |
Arbitrary command execution via exec endpoint |
get_route_to |
ip route show via exec endpoint |
Not implemented (raise NotImplementedError):
get_bgp_config, get_bgp_neighbors, get_bgp_neighbors_detail,
get_optics, get_probes_config, get_probes_results, get_firewall_policies
Optional arguments
| Argument | Type | Default | Description |
|---|---|---|---|
port |
int | 8006 |
Proxmox API port |
verify_ssl |
bool | True |
Verify TLS certificate |
realm |
str | "pam" |
PAM realm for password auth |
node |
str | auto | Override target node name |
token_name |
str | None |
API token identifier (user@realm!tokenid) |
token_value |
str | None |
API token secret |
Development
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
# Run tests with coverage
pytest --cov=napalm_proxmox --cov-report=term-missing
# Lint
ruff check napalm_proxmox
Architecture
napalm_proxmox/
├── __init__.py # Exports ProxmoxDriver
├── driver.py # ProxmoxDriver — all NAPALM methods
└── utils.py # MAC normalisation, CIDR parsing, ARP/OVS parsers
tests/
├── conftest.py # Shared fixtures and mock API payloads
├── test_connection.py # open / close / is_alive
├── test_get_facts.py # get_facts
├── test_interfaces.py # get_interfaces, get_interfaces_ip, get_interfaces_counters
├── test_environment.py # get_environment
├── test_sdn.py # get_vlans, get_network_instances
├── test_ovs_and_arp.py # get_arp_table, get_mac_address_table, ARP/OVS utils
├── test_misc.py # NTP, SNMP, users, config, ping, traceroute, CLI, LLDP
└── test_utils.py # Unit tests for napalm_proxmox.utils
License
Apache License 2.0 — see LICENSE.
Languages
Python
100%