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.
68 lines
2.0 KiB
Python
68 lines
2.0 KiB
Python
"""to_plain: pyVmomi objects -> JSON-safe Python values.
|
|
|
|
Every parser in this package works on the output of ``to_plain`` rather than
|
|
on pyVmomi objects. That is what lets the harvest tool dump a real host to
|
|
JSON and the tests feed that same JSON back in.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import datetime
|
|
import json
|
|
|
|
from pyVmomi import vim
|
|
|
|
from napalm_vmware._plain import to_plain
|
|
|
|
|
|
def test_scalars_pass_through():
|
|
assert to_plain("a") == "a"
|
|
assert to_plain(3) == 3
|
|
assert to_plain(True) is True
|
|
assert to_plain(None) is None
|
|
|
|
|
|
def test_enum_becomes_plain_str():
|
|
value = to_plain(vim.VirtualMachine.PowerState.poweredOn)
|
|
assert value == "poweredOn"
|
|
assert type(value) is str
|
|
|
|
|
|
def test_managed_object_becomes_its_moref():
|
|
assert to_plain(vim.HostSystem("host-21", None)) == "host-21"
|
|
|
|
|
|
def test_data_object_becomes_dict_with_type_and_without_unset_fields():
|
|
nic = vim.host.PhysicalNic(
|
|
device="vmnic0",
|
|
linkSpeed=vim.host.PhysicalNic.LinkSpeedDuplex(speedMb=1000, duplex=True),
|
|
)
|
|
plain = to_plain(nic)
|
|
assert plain["_type"] == "PhysicalNic"
|
|
assert plain["device"] == "vmnic0"
|
|
assert plain["linkSpeed"] == {"_type": "PhysicalNicLinkInfo", "speedMb": 1000, "duplex": True}
|
|
assert "driver" not in plain # optional and unset
|
|
|
|
|
|
def test_dynamic_property_noise_is_dropped():
|
|
assert "dynamicProperty" not in to_plain(vim.host.PhysicalNic(device="vmnic0"))
|
|
|
|
|
|
def test_lists_are_converted_elementwise():
|
|
devices = [vim.vm.device.VirtualVmxnet3(key=4000), vim.vm.device.VirtualDisk(key=2000)]
|
|
assert [d["_type"] for d in to_plain(devices)] == ["VirtualVmxnet3", "VirtualDisk"]
|
|
|
|
|
|
def test_datetime_becomes_epoch_seconds():
|
|
when = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
|
|
assert to_plain(when) == 1767225600.0
|
|
|
|
|
|
def test_bytes_are_dropped_to_none():
|
|
assert to_plain(b"\x00\x01") is None
|
|
|
|
|
|
def test_result_is_json_serialisable():
|
|
nic = vim.host.PhysicalNic(device="vmnic0", pci="0000:0b:00.0")
|
|
json.dumps(to_plain([nic, vim.HostSystem("host-1", None), 1.5]))
|