Files
napalm-vmware/napalm_vmware/parse/interfaces.py
T
Christian Manivong 06b446934f fix: what bandit and the remaining lint rules found
- zip() in provisioning is strict (B905): the OVF asks for one adapter
  per requested NIC, so the two lists match. A VM that came back with
  fewer adapters used to get a seed that left a NIC unconfigured; it now
  fails, and create_vm_from_cloud_init discards it as on any other error
  after the import. A test covers it.
- A failed abort of the import lease is logged at debug level instead of
  passed over (B110); the original error is still the one raised.
- nosec for false positives, each with its reason: the 0.0.0.0 entries in
  the parsers are addresses vSphere reports, not binds (B104); qemu-img
  runs with a fixed argv and no shell (B404, B603); xml.sax.saxutils only
  escapes the OVF descriptor the driver writes, nothing is parsed (B406),
  so defusedxml would not apply.
2026-10-23 08:38:41 +02:00

89 lines
3.2 KiB
Python

"""NAPALM interface getters for an ESXi host.
A host has two kinds of interface worth reporting: physical uplinks
(``vmnicN``) and VMkernel adapters (``vmkN``), which carry the host's own IP
addresses. Port groups are not interfaces; they are reported by
``get_virtual_networks``.
"""
from __future__ import annotations
import ipaddress
from typing import Any
_DEFAULT_MTU = 1500
# Addresses vSphere reports for an interface without one; data, not a bind.
_UNASSIGNED = {"", "0.0.0.0"} # nosec B104
def _uplink_mtus(host: dict[str, Any]) -> dict[str, int]:
"""pnic key -> MTU of the standard vSwitch that uses it as an uplink."""
mtus: dict[str, int] = {}
for vswitch in host.get("config.network.vswitch") or []:
for pnic_key in vswitch.get("pnic", []):
mtus[pnic_key] = int(vswitch.get("mtu") or _DEFAULT_MTU)
return mtus
def _iface(up: bool, description: str, speed: float, mtu: int, mac: str) -> dict[str, Any]:
return {
"is_up": up,
"is_enabled": True,
"description": description,
"last_flapped": -1.0,
"speed": speed,
"mtu": mtu,
"mac_address": mac.upper(),
}
def host_interfaces(host: dict[str, Any]) -> dict[str, dict[str, Any]]:
"""vmnics and vmks in the shape of NAPALM's ``get_interfaces``."""
mtus = _uplink_mtus(host)
result: dict[str, dict[str, Any]] = {}
for pnic in host.get("config.network.pnic") or []:
link = pnic.get("linkSpeed") or {}
description = " ".join(p for p in (pnic.get("driver"), pnic.get("pci")) if p)
result[pnic["device"]] = _iface(
up=bool(link),
description=description,
speed=float(link.get("speedMb", 0)),
mtu=mtus.get(pnic.get("key", ""), _DEFAULT_MTU),
mac=pnic.get("mac", ""),
)
for vnic in host.get("config.network.vnic") or []:
spec = vnic.get("spec") or {}
result[vnic["device"]] = _iface(
up=True,
description=vnic.get("portgroup") or spec.get("portgroup", ""),
speed=0.0,
mtu=int(spec.get("mtu") or _DEFAULT_MTU),
mac=spec.get("mac", ""),
)
return result
def _ipv4(ip: dict[str, Any]) -> dict[str, dict[str, int]]:
address = ip.get("ipAddress", "")
if address in _UNASSIGNED:
return {}
network = ipaddress.IPv4Network(f"0.0.0.0/{ip.get('subnetMask') or '32'}")
return {address: {"prefix_length": network.prefixlen}}
def _ipv6(ip: dict[str, Any]) -> dict[str, dict[str, int]]:
entries = (ip.get("ipV6Config") or {}).get("ipV6Address", [])
return {e["ipAddress"]: {"prefix_length": int(e.get("prefixLength", 128))} for e in entries}
def host_interfaces_ip(host: dict[str, Any]) -> dict[str, dict[str, Any]]:
"""Addresses of the VMkernel adapters, as NAPALM's ``get_interfaces_ip``."""
result: dict[str, dict[str, Any]] = {}
for vnic in host.get("config.network.vnic") or []:
ip = (vnic.get("spec") or {}).get("ip") or {}
families = {"ipv4": _ipv4(ip), "ipv6": _ipv6(ip)}
families = {k: v for k, v in families.items() if v}
if families:
result[vnic["device"]] = families
return result