Files
napalm-proxmox/tests/conftest.py
T
Christian Manivong 20fcf2ebc3 fix: four real defects the fourteen failing tests were pointing at
Closes netork#115.

The suite had been red long enough that it stopped being read. Four of the
fourteen failures were the tests being right.

`interfaces_mixin.py` used `re.match` without importing `re`, so
`get_mac_address_table` raised NameError against any node with a Linux bridge.
The tests never reached that line: they mocked the API call underneath
`_exec_ssh_command`, which takes two positional arguments where the doubles
accepted one, and which base64-wraps the command — so a fixture keyed on
"bridge fdb" appearing in the text matched nothing and the helper returned "".
They mock `_exec_ssh_command` itself now, which is the driver's own seam.

`is_alive` called `_resolve_node()`, which returns early without touching the
API whenever a node was configured through optional_args. A dead connection
reported itself alive. It probes `GET /version` now.

The documented `realm` optional_arg was read into `self._realm` in `__init__`
and then never used. Proxmox authenticates against "<user>@<realm>" and rejects
a bare username, so the option had no effect and callers had to know to type the
realm themselves.

`get_vlans` filtered out entries with no member ports on one return path while
the OVS path returned them, so a configured SDN VNet was visible or invisible
depending on which branch ran. A VNet exists on the node whether or not anything
is attached to it, and netOrk's VLAN discovery reads this.

`get_ipv6_neighbors_table` was simply missing and fell through to NAPALM's stub;
it is implemented against `ip -6 neigh show`, dropping FAILED entries.

The rest were stale tests. The DNS fixture put an FQDN where a search domain
belongs, which made `get_facts` build "pve1.pve1.example.com" and look like a
driver bug. The LLDP fixture was a simplified shape that real `lldpcli show
neighbors summary` does not produce — the parser matches on the ", via: LLDP"
that follows the interface name. And `test_bridge_vlan_show_parsing` covered a
fallback that was replaced by VM-config scanning, asserting an "interfaces" key
this method has never returned; it is now a test of the fallback that exists.
2026-08-21 13:22:03 +07:00

221 lines
5.9 KiB
Python

"""Shared fixtures and mock helpers for napalm-proxmox tests."""
from __future__ import annotations
import pytest
from unittest.mock import MagicMock, patch
from napalm_proxmox.driver import ProxmoxDriver
# ---------------------------------------------------------------------------
# Sample API payloads
# ---------------------------------------------------------------------------
NODE_STATUS = {
"uptime": 123456,
"model": "PowerEdge R640",
"cpu": 0.12,
"memory": {"total": 137438953472, "used": 68719476736},
}
VERSION_INFO = {"version": "8.2.4", "release": "8", "repoid": "abc123"}
NODE_NETWORK = [
{
"iface": "eth0",
"type": "eth",
"active": 1,
"autostart": 1,
"hwaddr": "aa:bb:cc:dd:ee:01",
"speed": 1000,
"mtu": 1500,
"address": "10.0.0.1",
"netmask": "255.255.255.0",
"comments": "Management",
},
{
"iface": "eth1",
"type": "eth",
"active": 1,
"autostart": 1,
"hwaddr": "aa:bb:cc:dd:ee:02",
"speed": 10000,
"mtu": 9000,
"comments": "",
},
{
"iface": "vmbr0",
"type": "bridge",
"active": 1,
"autostart": 1,
"hwaddr": "aa:bb:cc:dd:ee:03",
"speed": None,
"mtu": 1500,
"address": "192.168.1.1",
"netmask": "255.255.255.0",
"comments": "Linux bridge",
},
{
"iface": "vmbr1",
"type": "OVSBridge",
"active": 0,
"autostart": 1,
"hwaddr": "",
"speed": None,
"mtu": 1500,
"comments": "OVS bridge",
},
]
SDN_ZONES = [
{"zone": "zone-vlan", "type": "vlan", "nodes": "pve1"},
{"zone": "zone-vxlan", "type": "vxlan", "nodes": "pve1,pve2", "vrf-vxlan": 10000},
{"zone": "zone-evpn", "type": "evpn", "nodes": "pve1,pve2", "vrf-vxlan": 10001},
]
SDN_VNETS = [
{"vnet": "vnet0", "zone": "zone-vlan", "tag": 10},
{"vnet": "vnet1", "zone": "zone-vlan", "tag": 20},
{"vnet": "vnet2", "zone": "zone-vxlan", "tag": 100000},
]
SDN_SUBNETS_VNET0 = [
{"cidr": "10.10.0.0/24", "gateway": "10.10.0.1", "vnet": "vnet0"},
]
SDN_SUBNETS_VNET1 = [
{"cidr": "10.20.0.0/24", "gateway": "10.20.0.1", "vnet": "vnet1"},
{"cidr": "2001:db8:20::/64", "gateway": "2001:db8:20::1", "vnet": "vnet1"},
]
SDN_SUBNETS_VNET2: list = []
# A DNS *search domain*, not an FQDN. It used to read "pve1.example.com",
# which made get_facts build "pve1.pve1.example.com" and looked like a
# driver bug rather than bad test data.
DNS_INFO = {"search": "example.com", "dns1": "8.8.8.8"}
NTP_INFO = {"server": "pool.ntp.org,time.cloudflare.com"}
NODE_NETSTAT = [
{
"dev": "eth0",
"rx_bytes": 1000000,
"tx_bytes": 500000,
"rx_packets": 1000,
"tx_packets": 900,
"rx_errs": 0,
"tx_errs": 0,
"rx_drop": 0,
"tx_drop": 1,
}
]
NODES_LIST = [{"node": "pve1", "status": "online"}]
PVE_USERS = [
{"userid": "root@pam", "groups": ""},
{"userid": "napalm@pam", "groups": ""},
]
# ---------------------------------------------------------------------------
# Driver fixture
# ---------------------------------------------------------------------------
def _build_mock_api(
nodes=None,
node_status=None,
version=None,
network=None,
sdn_zones=None,
sdn_vnets=None,
sdn_subnets=None,
dns=None,
ntp=None,
netstat=None,
pve_users=None,
exec_return="",
sensors=None,
):
"""Build a MagicMock ProxmoxAPI with pre-configured return values."""
api = MagicMock()
# Nodes list
api.nodes.get.return_value = nodes or NODES_LIST
# Version
api.version.get.return_value = version or VERSION_INFO
# Node sub-resource
node_res = MagicMock()
api.nodes.return_value = node_res
node_res.status.get.return_value = node_status or NODE_STATUS
node_res.network.get.return_value = network if network is not None else NODE_NETWORK
node_res.dns.get.return_value = dns or DNS_INFO
node_res.ntp.get.return_value = ntp or NTP_INFO
node_res.netstat.get.return_value = netstat or NODE_NETSTAT
node_res.execute.post.return_value = {"data": exec_return}
node_res.hardware.sensors.get.return_value = sensors or []
node_res.time.get.return_value = {}
node_res.subscription.get.return_value = {}
# SDN
cluster = MagicMock()
api.cluster = cluster
cluster.sdn.zones.get.return_value = sdn_zones or SDN_ZONES
cluster.sdn.vnets.get.return_value = sdn_vnets or SDN_VNETS
# SDN subnets per vnet
subnets_map = sdn_subnets or {
"vnet0": SDN_SUBNETS_VNET0,
"vnet1": SDN_SUBNETS_VNET1,
"vnet2": SDN_SUBNETS_VNET2,
}
def _vnet_resource(vnet_id):
vnet_res = MagicMock()
vnet_res.subnets.get.return_value = subnets_map.get(vnet_id, [])
return vnet_res
cluster.sdn.vnets.side_effect = _vnet_resource
# Access / users
api.access.users.get.return_value = pve_users or PVE_USERS
api.access.users.return_value.get.return_value = {}
return api
@pytest.fixture
def driver():
"""Return an open ProxmoxDriver with a mocked API."""
drv = ProxmoxDriver(
hostname="pve1.example.com",
username="root",
password="secret",
optional_args={"verify_ssl": False, "realm": "pam"},
)
mock_api = _build_mock_api()
with patch("napalm_proxmox.driver.ProxmoxAPI", return_value=mock_api):
drv.open()
return drv
@pytest.fixture
def driver_with_exec(request):
"""Return a driver whose exec endpoint returns a parametrised string."""
exec_output = getattr(request, "param", "")
drv = ProxmoxDriver(
hostname="pve1.example.com",
username="root",
password="secret",
optional_args={"verify_ssl": False},
)
mock_api = _build_mock_api(exec_return=exec_output)
with patch("napalm_proxmox.driver.ProxmoxAPI", return_value=mock_api):
drv.open()
return drv