Files
napalm-vmware/tests/test_plain.py
T
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

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]))