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

96 lines
3.6 KiB
Python

"""get_facts for an ESXi host and for a vCenter."""
from __future__ import annotations
import pytest
from napalm_vmware.parse.facts import esxi_facts, host_serial, vcenter_facts
class TestEsxiFacts:
def test_from_vcsim(self, esxi_data, esxi_host):
facts = esxi_facts(esxi_data["about"], esxi_host)
assert facts == {
"hostname": "localhost",
"fqdn": "localhost.localdomain",
"vendor": "VMware, Inc.",
"model": "VMware Virtual Platform",
"serial_number": "VMware-56 4d 8d e8 1e 9f a1 3e-71 fa 13 a8 e1 a7 fd 70",
"os_version": "VMware ESXi 8.0.2 build-21997540",
"uptime": 77229.0,
"interface_list": ["vmk0", "vmnic0", "vmnic1"],
}
def test_falls_back_to_the_inventory_name_without_dns_config(self, esxi_data, esxi_host):
del esxi_host["config.network.dnsConfig"]
facts = esxi_facts(esxi_data["about"], esxi_host)
assert facts["hostname"] == "localhost"
assert facts["fqdn"] == "localhost.localdomain"
def test_hostname_without_domain(self, esxi_data, esxi_host):
esxi_host["config.network.dnsConfig"]["domainName"] = ""
assert esxi_facts(esxi_data["about"], esxi_host)["fqdn"] == "localhost"
def test_unknown_uptime_is_minus_one(self, esxi_data, esxi_host):
del esxi_host["summary.quickStats"]
assert esxi_facts(esxi_data["about"], esxi_host)["uptime"] == -1.0
def test_os_version_falls_back_to_about(self, esxi_data, esxi_host):
del esxi_host["config.product"]
facts = esxi_facts(esxi_data["about"], esxi_host)
assert facts["os_version"] == esxi_data["about"]["fullName"]
class TestHostSerial:
def _info(self, *pairs):
return {
"otherIdentifyingInfo": [
{"identifierType": {"key": k}, "identifierValue": v} for k, v in pairs
]
}
def test_system_info_serial_wins(self):
host = {
"hardware.systemInfo": {"serialNumber": "ABC123", **self._info(("ServiceTag", "x"))}
}
assert host_serial(host) == "ABC123"
@pytest.mark.parametrize(
("pairs", "expected"),
[
((("ServiceTag", "SVC"), ("SerialNumberTag", "SER")), "SER"),
((("EnclosureSerialNumberTag", "ENC"), ("ServiceTag", "SVC")), "ENC"),
((("AssetTag", "No Asset Tag"),), ""),
],
)
def test_identifying_info_by_preference(self, pairs, expected):
assert host_serial({"hardware.systemInfo": self._info(*pairs)}) == expected
def test_falls_back_to_summary_hardware(self):
host = {"summary.hardware": self._info(("SerialNumberTag", "OLD"))}
assert host_serial(host) == "OLD"
def test_blank_values_are_ignored(self):
host = {"hardware.systemInfo": self._info(("SerialNumberTag", " "), ("ServiceTag", "S"))}
assert host_serial(host) == "S"
class TestVcenterFacts:
def test_from_vcsim(self, vcenter_data):
facts = vcenter_facts(vcenter_data["about"], "vc01.example.lan")
assert facts == {
"hostname": "vc01",
"fqdn": "vc01.example.lan",
"vendor": "VMware, Inc.",
"model": "VMware vCenter Server",
"serial_number": "35bb7c82-8526-5aa7-a886-cd7f8e6be786",
"os_version": "VMware vCenter Server 6.5.0 build-5973321 (govmomi simulator)",
"uptime": -1.0,
"interface_list": [],
}
def test_ip_address_is_not_split_into_a_hostname(self, vcenter_data):
facts = vcenter_facts(vcenter_data["about"], "10.0.0.5")
assert facts["hostname"] == "10.0.0.5"
assert facts["fqdn"] == "10.0.0.5"