Files
napalm-vmware/tests/test_parse_networks.py
Christian Manivong c8e472b4c6 feat: NAPALM drivers for VMware ESXi and vCenter
Two drivers from one package, both in the hypervisor role:

- vmware_esxi talks to one host directly: facts, vmnics and vmkernel
  NICs, CDP/LLDP neighbours, sensors, VMs, datastores and port groups.
- vmware_vcenter talks to a vCenter: every VM of every host it manages,
  with the host as the VM's node, plus distributed port groups. It
  reports no interfaces of its own; the hosts' NICs belong to the hosts.

Both implement the HypervisorDriver VM contract: get_vms, get_vm_config,
start/stop/reboot/suspend_vm and the four snapshot methods, and emit raw
device warnings (maintenance mode, disconnected host, config issues,
host managed by a vCenter, free license making the API read-only).

Every read is a PropertyCollector query for the explicit paths in
paths.py, converted by to_plain() into dicts and lists; the parsers
only ever see that. tools/harvest.py dumps exactly those paths to JSON
and tools/sanitize.py scrubs the dump, so a real host can become a test
fixture without code changes. A VM's vmid is its instance UUID, which
survives vMotion and re-registration; a MoRef does not.

Tested against govmomi's vcsim in ESXi and vCenter mode, including real
power and snapshot tasks. Not yet tested against real hardware.
2026-09-24 09:07:30 +02:00

127 lines
4.0 KiB
Python

"""Port groups, distributed port groups, and which one a VM NIC is on."""
from __future__ import annotations
import pytest
from napalm_vmware.parse.networks import (
NetworkIndex,
dv_vlan,
network_index,
nic_network,
virtual_networks,
)
@pytest.fixture
def vc_index(vcenter_data):
objs = vcenter_data["objects"]
return network_index(objs["HostSystem"], objs["DistributedVirtualPortgroup"])
class TestDvVlan:
@pytest.mark.parametrize(
("vlan", "expected"),
[
({"_type": "VmwareDistributedVirtualSwitchVlanIdSpec", "vlanId": 30}, 30),
({"_type": "VmwareDistributedVirtualSwitchTrunkVlanSpec", "vlanId": []}, 0),
({"_type": "VmwareDistributedVirtualSwitchPvlanSpec", "pvlanId": 101}, 101),
],
)
def test_kinds(self, vlan, expected):
assert dv_vlan({"vlan": vlan}) == expected
def test_missing(self):
assert dv_vlan(None) == 0
class TestNetworkIndex:
def test_standard_and_distributed(self, vc_index):
assert vc_index.standard["VM Network"] == 0
assert vc_index.distributed["dvportgroup-12"] == ("DC0_DVPG0", 0)
def test_standard_vlan(self, esxi_host):
esxi_host["config.network.portgroup"][0]["spec"]["vlanId"] = 40
index = network_index([esxi_host], [])
assert index.standard["VM Network"] == 40
class TestNicNetwork:
def test_standard_backing(self):
index = NetworkIndex(standard={"VM Network": 20}, distributed={})
nic = {
"backing": {
"_type": "VirtualEthernetCardNetworkBackingInfo",
"deviceName": "VM Network",
}
}
assert nic_network(nic, index) == ("VM Network", 20)
def test_distributed_backing(self, vc_index):
nic = {
"backing": {
"_type": "VirtualEthernetCardDistributedVirtualPortBackingInfo",
"port": {"portgroupKey": "dvportgroup-12"},
}
}
assert nic_network(nic, vc_index) == ("DC0_DVPG0", 0)
def test_unknown_distributed_portgroup_keeps_its_key(self, vc_index):
nic = {
"backing": {
"_type": "VirtualEthernetCardDistributedVirtualPortBackingInfo",
"port": {"portgroupKey": "dvportgroup-99"},
}
}
assert nic_network(nic, vc_index) == ("dvportgroup-99", 0)
def test_nsx_opaque_network(self, vc_index):
nic = {
"backing": {
"_type": "VirtualEthernetCardOpaqueNetworkBackingInfo",
"opaqueNetworkId": "ls-web",
}
}
assert nic_network(nic, vc_index) == ("ls-web", 0)
def test_no_backing(self, vc_index):
assert nic_network({}, vc_index) == ("", 0)
class TestVirtualNetworks:
def test_esxi_standard_portgroups(self, esxi_host):
nets = virtual_networks([esxi_host], [], [])
assert nets["VM Network"] == {
"name": "VM Network",
"type": "portgroup",
"bridge": "vSwitch0",
"vlan_id": 0,
"autostart": True,
"active": True,
}
assert set(nets) == {"VM Network", "Management Network"}
def test_vcenter_adds_distributed_portgroups_but_not_uplinks(self, vcenter_data):
objs = vcenter_data["objects"]
nets = virtual_networks(
objs["HostSystem"],
objs["DistributedVirtualPortgroup"],
objs["DistributedVirtualSwitch"],
)
assert nets["DC0_DVPG0"] == {
"name": "DC0_DVPG0",
"type": "dvportgroup",
"bridge": "DVS0",
"vlan_id": 0,
"autostart": True,
"active": True,
}
assert "DVS0-DVUplinks-8" not in nets
def test_uplink_flag_wins_over_the_name(self, vcenter_data):
objs = vcenter_data["objects"]
pgs = objs["DistributedVirtualPortgroup"]
pgs[1]["config.uplink"] = True
nets = virtual_networks([], pgs, objs["DistributedVirtualSwitch"])
assert "DC0_DVPG0" not in nets