"""NAPALM driver for a VMware ESXi host, addressed directly.""" from __future__ import annotations from typing import Any from napalm_device_types import FingerprintRule from pyVmomi import vim from napalm_vmware._plain import to_plain from napalm_vmware._tasks import invoke from napalm_vmware.actions import VmwareActionsMixin from napalm_vmware.base import VmwareBaseDriver from napalm_vmware.parse.facts import esxi_facts from napalm_vmware.parse.interfaces import host_interfaces, host_interfaces_ip from napalm_vmware.parse.lldp import lldp_neighbors from napalm_vmware.provisioning import VmwareProvisioningMixin class VmwareEsxiDriver(VmwareProvisioningMixin, VmwareActionsMixin, VmwareBaseDriver): """One ESXi host. Its VMs, vmnics, vmkernel NICs, datastores and port groups.""" DRIVER_NAME = "vmware_esxi" API_TYPE = "HostAgent" STANDALONE = True # A host reboot runs through hardware POST before ESXi starts its services. REBOOT_SETTLE_SECONDS = 300 # Unverified against real hardware: the Host Client landing page says # "VMware ESXi"; a vCenter's does not, and it says "vCenter". HTTP_FINGERPRINT = [ FingerprintRule("vmware esxi", weight=9.0, mandatory=True), FingerprintRule("vcenter", weight=9.0, negative=True), ] def _host(self) -> dict[str, Any]: hosts = self._hosts() if not hosts: raise RuntimeError(f"{self.hostname} shows no host in its inventory") return hosts[0] def get_facts(self) -> dict[str, Any]: return esxi_facts(self._inventory.about(), self._host()) def get_interfaces(self) -> dict[str, dict[str, Any]]: return host_interfaces(self._host()) def get_interfaces_ip(self) -> dict[str, dict[str, Any]]: return host_interfaces_ip(self._host()) def reboot_host(self) -> None: """Restart the host, which has to be in maintenance mode. ``force=True`` would restart a host with running VMs by cutting their power. Evacuating them -- or deciding not to -- is the operator's call, made by entering maintenance mode in vSphere first. """ host = self._host() if not host.get("runtime.inMaintenanceMode"): raise RuntimeError( f"{host.get('name', self.hostname)} is not in maintenance mode; " "enter it in vSphere first so running VMs are not powered off" ) # The task finishes as the host goes down; there is nothing to wait for. invoke(self._mo(vim.HostSystem, host["_moref"]).RebootHost_Task, force=False) def get_lldp_neighbors(self) -> dict[str, list[dict[str, str]]]: network_system = self._host().get("configManager.networkSystem") if not network_system: return {} mo = self._mo(vim.host.NetworkSystem, network_system) try: hints = invoke(mo.QueryNetworkHint) except RuntimeError: return {} return lldp_neighbors(to_plain(hints) or [])