fix: get_dhcp_leases raises when no lease source answered (#27)
CI / test (3.10) (push) Successful in 32s
CI / test (3.11) (push) Successful in 34s
CI / test (3.12) (push) Successful in 33s
CI / test (3.10) (pull_request) Successful in 32s
CI / test (3.11) (pull_request) Successful in 29s
CI / test (3.12) (pull_request) Successful in 34s
CI / test (3.10) (push) Successful in 32s
CI / test (3.11) (push) Successful in 34s
CI / test (3.12) (push) Successful in 33s
CI / test (3.10) (pull_request) Successful in 32s
CI / test (3.11) (pull_request) Successful in 29s
CI / test (3.12) (pull_request) Successful in 34s
When Kea, ISC DHCP and the ARP fallback all failed, get_dhcp_leases() returned [], which a caller cannot tell from a server with no leases. It now raises a RuntimeError naming each failure, and returns [] only when a source answered with nothing. An empty ARP table after both lease sources failed counts as unread too, since ARP rows are no lease data. Closes #27
This commit is contained in:
1 parent
6a91f43fc3
commit
5a6e573300
2 files changed
+135
-5
No files matched your search
@@ -1316,7 +1316,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:
|
||||
|
||||
@@ -1349,6 +1358,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")
|
||||
@@ -1356,8 +1368,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:
|
||||
@@ -1368,20 +1382,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).
|
||||
|
||||
Reference in new issue
Block a user