Files
napalm-vmware/napalm_vmware/_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

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