CI / test (3.10) (push) Successful in 30s
CI / test (3.11) (push) Successful in 29s
CI / test (3.12) (push) Successful in 30s
CI / test (3.10) (pull_request) Successful in 29s
CI / test (3.11) (pull_request) Successful in 28s
CI / test (3.12) (pull_request) Successful in 30s
get_virtual_ips() reads every address bound to a CARP VHID -- the CARP address and IP aliases on its VHID alike -- from /api/interfaces/overview/export, with this node's CARP state, so the reading says who holds each address. interfaces/vip_settings would list the same addresses with each VHID's CARP password; it is not read. vip_manager and keepalived are Linux mechanisms and are None here. Needs napalm-device-types 4.10.0 (carp_virtual_ip). netOrk #1121.
64 lines
2.5 KiB
Python
64 lines
2.5 KiB
Python
"""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)
|