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.
96 lines
3.6 KiB
Python
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"
|