9264cdcba94e78c1bdf20e9a27f9390546ba36d0
Replaces the template-clone flow with: create empty VM shell, download the cloud image on the node (cached by filename, optional checksum verification), qm importdisk, attach as scsi0. NIC config, snippet upload, ssh keys, disk resize, and start remain unchanged (already generic). New helpers: _run_node_command (strict SSH exec with custom timeout and non-zero-exit detection, unlike the best-effort _exec_ssh_command), _download_cloud_image (idempotent download + checksum check), _find_default_image_storage (content=images discovery, mirrors the existing snippet-storage discovery). 24 tests pass (10 new: _run_node_command x2, _download_cloud_image x4, plus rewrites of the 4 existing create_vm_from_cloud_init tests for the new flow).
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%