"""``get_vms``: one VMDict per virtual machine, plus the keys netOrk reads. Beyond the ``VMDict`` contract, netOrk's VM sync expects ``type``, ``interfaces`` (NAPALM shape plus ``ipv4``/``bridge``/``tag``), ``ipv4``, ``agent_enabled``/``agent_running`` (here: VMware Tools), ``disks`` and ``onboot``. ``moref`` is added so an operator can find the VM in vSphere. ``vmid`` is the VM's *instance* UUID: stable across vMotion and re-registration, unique within a vCenter, and the key ``SearchIndex.FindByUuid`` looks VMs up by. The MoRef is none of those things. """ from __future__ import annotations import ipaddress from typing import Any from napalm_vmware.parse import vm_devices as dev from napalm_vmware.parse.networks import NetworkIndex, nic_network _STATUS = {"poweredOn": "running", "poweredOff": "stopped", "suspended": "suspended"} def _is_ipv4(address: str) -> bool: try: return isinstance(ipaddress.ip_address(address), ipaddress.IPv4Address) except ValueError: return False def _cpu_usage(vm: dict[str, Any], host_mhz: int) -> float: used = (vm.get("summary.quickStats") or {}).get("overallCpuUsage") or 0 capacity = int(vm.get("config.hardware.numCPU") or 0) * host_mhz if not capacity: return 0.0 return round(min(used / capacity, 1.0), 4) def _interfaces(vm: dict[str, Any], index: NetworkIndex) -> dict[str, dict[str, Any]]: guest_nics = {n.get("deviceConfigId"): n for n in vm.get("guest.net") or []} result: dict[str, dict[str, Any]] = {} for nic in filter(dev.is_nic, dev.devices(vm)): network, vlan = nic_network(nic, index) guest = guest_nics.get(nic.get("key"), {}) connected = guest.get("connected", (nic.get("connectable") or {}).get("connected", False)) ipv4 = next((a for a in guest.get("ipAddress", []) if _is_ipv4(a)), "") result[dev.label(nic)] = { "is_up": bool(connected), "is_enabled": True, "description": network, "mac_address": nic.get("macAddress", "").upper(), "speed": -1.0, "mtu": 1500, "last_flapped": -1.0, "ipv4": ipv4, "bridge": network, "tag": vlan or None, } return result def _autostart_vms(hosts: list[dict[str, Any]]) -> set[str]: """MoRefs of VMs the host's autostart manager will power on at boot.""" morefs: set[str] = set() for host in hosts: config = host.get("config.autoStart") or {} if not (config.get("defaults") or {}).get("enabled"): continue for entry in config.get("powerInfo", []): if str(entry.get("startAction", "")).lower() == "poweron": morefs.add(entry.get("key", "")) return morefs def _vm( vm: dict[str, Any], hosts: dict[str, dict[str, Any]], index: NetworkIndex, onboot: bool ) -> dict[str, Any]: host = hosts.get(vm.get("runtime.host", ""), {}) stats = vm.get("summary.quickStats") or {} interfaces = _interfaces(vm, index) primary = vm.get("guest.ipAddress") or "" ipv4 = ( primary if _is_ipv4(primary) else next((i["ipv4"] for i in interfaces.values() if i["ipv4"]), "") ) return { "name": vm.get("name", ""), "vmid": vm["config.instanceUuid"], "moref": vm["_moref"], "type": "vm", "status": _STATUS.get(vm.get("runtime.powerState", ""), "unknown"), "vcpus": int(vm.get("config.hardware.numCPU") or 0), "memory": int(vm.get("config.hardware.memoryMB") or 0), "cpu_usage": _cpu_usage(vm, int((host.get("summary.hardware") or {}).get("cpuMhz") or 0)), "memory_usage": int(stats.get("guestMemoryUsage") or 0), "uptime": int(stats.get("uptimeSeconds") or 0), "node": host.get("name", ""), "interfaces": interfaces, "ipv4": ipv4, "agent_enabled": vm.get("guest.toolsStatus", "toolsNotInstalled") != "toolsNotInstalled", "agent_running": vm.get("guest.toolsRunningStatus") == "guestToolsRunning", "disks": [ {"name": dev.label(d), "size_mb": dev.disk_bytes(d) // (1024 * 1024)} for d in filter(dev.is_disk, dev.devices(vm)) ], "onboot": onboot, } def vm_list( vms: list[dict[str, Any]], hosts: list[dict[str, Any]], index: NetworkIndex ) -> list[dict[str, Any]]: """All VMs, templates and unidentifiable (config-less) VMs excluded.""" by_moref = {h["_moref"]: h for h in hosts} autostart = _autostart_vms(hosts) return [ _vm(vm, by_moref, index, vm["_moref"] in autostart) for vm in vms if vm.get("config.instanceUuid") and not vm.get("config.template") ]