"""The firewall's CARP addresses, read from its interface overview (netOrk #1121). ``/api/interfaces/overview/export`` lists every address of an interface; one bound to a CARP VHID carries ``vhid`` and this node's CARP ``status``. That covers the CARP address itself and an IP alias bound to its VHID alike -- both float with the VHID's MASTER. An address without a VHID stays on this node and is no virtual IP. ``interfaces/vip_settings/search_item`` would list the same addresses, but with each VHID's CARP password in it. The overview has none, so a secret never reaches the caller. What a state means (MASTER holds, BACKUP stands by) is the same on every CARP firewall: :func:`napalm_device_types.virtual_ips.carp_virtual_ip`. """ from __future__ import annotations import ipaddress from typing import Any from napalm_device_types.models import VirtualIpDict from napalm_device_types.virtual_ips import carp_virtual_ip def _interfaces(overview: Any) -> list[Any]: """The interface list, whether the answer is the list or wraps it.""" if isinstance(overview, dict): return overview.get("interfaces") or [] if isinstance(overview, list): return overview raise ValueError(f"unexpected interface overview: {type(overview).__name__}") def carp_addresses(overview: Any) -> list[VirtualIpDict]: """Every address bound to a CARP VHID in *overview*, sorted by address. :raises ValueError: when the answer is neither the interface list nor wraps it. """ entries: list[VirtualIpDict] = [] for interface in _interfaces(overview): if not isinstance(interface, dict): continue identifier = interface.get("identifier") or "" for family in ("ipv4", "ipv6"): for address in interface.get(family) or []: if not isinstance(address, dict) or not address.get("vhid"): continue entry = carp_virtual_ip( address=str(address.get("ipaddr") or ""), prefix_length=None, vhid=address.get("vhid"), state=address.get("status"), instance=interface.get("description") or identifier, interface=interface.get("device") or identifier or None, ) if entry is not None: entries.append(entry) return sorted(entries, key=_order) def _order(entry: VirtualIpDict) -> tuple[int, int]: address = ipaddress.ip_address(entry["address"]) return address.version, int(address)