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.
58 lines
1.9 KiB
Python
58 lines
1.9 KiB
Python
"""Convert pyVmomi objects into plain, JSON-safe Python values.
|
|
|
|
The drivers never parse pyVmomi objects directly. Everything fetched from the
|
|
vSphere API goes through :func:`to_plain` first, so a parser sees the same
|
|
shape whether its input came from a live host or from a JSON fixture that
|
|
``tools/harvest.py`` captured.
|
|
|
|
Conventions:
|
|
|
|
* a managed object (``HostSystem``, ``VirtualMachine`` ...) becomes its MoRef
|
|
id string, e.g. ``"host-21"``
|
|
* a data object becomes a dict of its set properties plus ``"_type"``, the
|
|
WSDL type name (``"VirtualDisk"``) -- device lists are told apart by it
|
|
* enums become ``str``, datetimes become epoch seconds, binary data ``None``
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import datetime
|
|
from typing import Any
|
|
|
|
from pyVmomi import VmomiSupport
|
|
|
|
_NOISE = frozenset({"dynamicType", "dynamicProperty"})
|
|
|
|
|
|
def to_plain(value: Any) -> Any:
|
|
"""Recursively convert ``value`` into dicts, lists and scalars."""
|
|
if value is None or isinstance(value, bool):
|
|
return value
|
|
if isinstance(value, VmomiSupport.ManagedObject):
|
|
return value._moId
|
|
if isinstance(value, VmomiSupport.DataObject):
|
|
return _data_object(value)
|
|
if isinstance(value, str):
|
|
return str(value)
|
|
if isinstance(value, (int, float)):
|
|
return value
|
|
if isinstance(value, datetime.datetime):
|
|
return value.timestamp()
|
|
if isinstance(value, (bytes, bytearray)):
|
|
return None
|
|
if isinstance(value, (list, tuple)):
|
|
return [to_plain(v) for v in value]
|
|
return str(value)
|
|
|
|
|
|
def _data_object(obj: Any) -> dict[str, Any]:
|
|
out: dict[str, Any] = {"_type": obj._wsdlName}
|
|
for prop in obj._GetPropertyList():
|
|
if prop.name in _NOISE:
|
|
continue
|
|
val = getattr(obj, prop.name, None)
|
|
if val is None or (isinstance(val, list) and not val):
|
|
continue
|
|
out[prop.name] = to_plain(val)
|
|
return out
|