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.
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
"""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}}}
|
||||
}
|
||||
Reference in New Issue
Block a user