Files
napalm-vmware/tests/test_parse_interfaces.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

81 lines
3.0 KiB
Python

"""get_interfaces / get_interfaces_ip for an ESXi host: vmnics and vmkernel NICs."""
from __future__ import annotations
from napalm_vmware.parse.interfaces import host_interfaces, host_interfaces_ip
class TestHostInterfaces:
def test_physical_nic_with_link(self, esxi_host):
vmnic0 = host_interfaces(esxi_host)["vmnic0"]
assert vmnic0 == {
"is_up": True,
"is_enabled": True,
"description": "nvmxnet3 0000:0b:00.0",
"last_flapped": -1.0,
"speed": 10000.0,
"mtu": 1500,
"mac_address": "00:0C:29:81:D8:A0",
}
def test_physical_nic_without_link_is_down(self, esxi_host):
esxi_host["config.network.pnic"][1].pop("linkSpeed", None)
vmnic1 = host_interfaces(esxi_host)["vmnic1"]
assert vmnic1["is_up"] is False
assert vmnic1["speed"] == 0.0
def test_mtu_comes_from_the_vswitch_the_nic_is_uplink_of(self, esxi_host):
esxi_host["config.network.vswitch"][0]["mtu"] = 9000
assert host_interfaces(esxi_host)["vmnic0"]["mtu"] == 9000
def test_nic_on_no_vswitch_has_default_mtu(self, esxi_host):
del esxi_host["config.network.vswitch"]
assert host_interfaces(esxi_host)["vmnic0"]["mtu"] == 1500
def test_vmkernel_nic(self, esxi_host):
assert host_interfaces(esxi_host)["vmk0"] == {
"is_up": True,
"is_enabled": True,
"description": "Management Network",
"last_flapped": -1.0,
"speed": 0.0,
"mtu": 1500,
"mac_address": "00:0C:29:81:D8:A0",
}
def test_host_without_network_config(self):
assert host_interfaces({"name": "h"}) == {}
class TestHostInterfacesIp:
def test_unassigned_address_is_left_out(self, esxi_host):
"""vcsim, like a DHCP vmk without a lease, reports 0.0.0.0."""
assert host_interfaces_ip(esxi_host) == {}
def test_ipv4_and_ipv6(self, esxi_host):
ip = esxi_host["config.network.vnic"][0]["spec"]["ip"]
ip["ipAddress"] = "192.0.2.10"
ip["subnetMask"] = "255.255.255.0"
ip["ipV6Config"] = {
"ipV6Address": [
{"ipAddress": "fe80::20c:29ff:fe81:d8a0", "prefixLength": 64},
{"ipAddress": "2001:db8::10", "prefixLength": 64},
]
}
assert host_interfaces_ip(esxi_host) == {
"vmk0": {
"ipv4": {"192.0.2.10": {"prefix_length": 24}},
"ipv6": {
"fe80::20c:29ff:fe81:d8a0": {"prefix_length": 64},
"2001:db8::10": {"prefix_length": 64},
},
}
}
def test_ipv6_only(self, esxi_host):
ip = esxi_host["config.network.vnic"][0]["spec"]["ip"]
ip["ipV6Config"] = {"ipV6Address": [{"ipAddress": "2001:db8::10", "prefixLength": 64}]}
assert host_interfaces_ip(esxi_host) == {
"vmk0": {"ipv6": {"2001:db8::10": {"prefix_length": 64}}}
}