Merge pull request 'fix: get_dhcp_leases raises when no lease source answered (#27)' (#29) from fix/dhcp-leases-raise-when-unread into master
CI / test (3.10) (push) Successful in 59s
CI / test (3.11) (push) Successful in 49s
CI / test (3.12) (push) Successful in 47s

This commit was merged in pull request #29.
This commit is contained in:
christianmanivong committed 2026-10-20 23:36:59 +00:00
commit 692a584fd2
2 files changed
+135 -5

No files matched your search

+26 -5
View File
@@ -1317,7 +1317,16 @@ class OPNsenseDriver(
2. **ISC DHCP** (legacy) — ``POST /api/dhcpv4/leases/searchlease``
Fields: ``mac``, ``address``, ``hostname``, ``ends``
3. **ARP table** fallback — ``GET /api/diagnostics/interface/get_arp``
Provides IP only, hostname will be empty.
Provides IP only, hostname will be empty, ``state`` is ``"arp"``.
The first source with leases wins. Without any, the ARP table stands
in -- also when Kea or ISC answered empty, since a box can hand out
addresses through a service neither covers. An empty list is a real
answer only: Kea or ISC answered with no leases and the fallback added
none. When neither answered and the fallback gives no rows (it failed,
or the table is empty), there is no answer to give, and this raises
:class:`RuntimeError` naming each failure -- a caller would otherwise
take ``[]`` for a server without leases and drop the ones it knows.
Each returned entry contains:
@@ -1350,6 +1359,9 @@ class OPNsenseDriver(
})
return result
failures: list[str] = []
answered = False # a lease source answered, with or without leases
# 1. Kea DHCPv4 plugin
try:
data = self._get("/api/kea/leases4/search")
@@ -1357,8 +1369,10 @@ class OPNsenseDriver(
leases = _parse_kea(rows)
if leases:
return leases
answered = True
except Exception as exc:
logger.debug("Kea DHCP lease fetch failed: %s", exc)
failures.append(f"Kea: {exc}")
# 2. ISC DHCP (legacy)
try:
@@ -1369,20 +1383,27 @@ class OPNsenseDriver(
leases = _parse_kea(data.get("rows", []))
if leases:
return leases
answered = True
except Exception as exc:
logger.debug("ISC DHCP lease fetch failed: %s", exc)
failures.append(f"ISC DHCP: {exc}")
# 3. ARP table fallback (IP only, no hostname)
fallback: list[dict[str, Any]] = []
try:
arp = self.get_arp_table()
return [
fallback = [
{"mac": e["mac"], "ip": e["ip"], "hostname": "", "lease_end": 0, "state": "arp"}
for e in arp
for e in self.get_arp_table()
if e.get("mac") and e.get("ip")
]
if not fallback:
failures.append("ARP table: no entries")
except Exception as exc:
logger.warning("ARP table fallback failed: %s", exc)
return []
failures.append(f"ARP table: {exc}")
if fallback or answered:
return fallback
raise RuntimeError("No DHCP lease source answered -- " + "; ".join(failures))
def create_dhcp_reservation(self, mac: str, ip: str, hostname: str = "") -> None:
"""Create (or update) a Kea DHCPv4 static reservation (MAC → IP).