feat(opnsense): add delete_dhcp_reservation_and_lease() for Kea DHCPv4
CI / test (3.10) (push) Failing after 7s
CI / test (3.11) (push) Failing after 7s
CI / test (3.12) (push) Failing after 7s
CI / test (3.9) (push) Failing after 7s

Combined removal of a static reservation and its active lease, needed by
NetOrk's VM-deletion cleanup flow. Reservation deletion follows the same
search-then-del<X>/{uuid} + reconfigure pattern as create_dhcp_reservation
and raises on failure; lease deletion is best-effort/non-fatal since the
Kea lease-delete endpoint shape is unverified against a real box.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Christian Manivong
2026-07-08 19:24:04 +02:00
co-authored by Claude Sonnet 5
parent c12065c114
commit b8dac1db63
2 changed files with 170 additions and 0 deletions
+97
View File
@@ -1340,3 +1340,100 @@ class TestCreateDhcpReservation:
driver.create_dhcp_reservation(mac="02:aa:bb:cc:dd:ee", ip="172.22.8.253")
driver.session.post.assert_not_called()
# ---------------------------------------------------------------------------
# delete_dhcp_reservation_and_lease()
# ---------------------------------------------------------------------------
class TestDeleteDhcpReservationAndLease:
def test_deletes_existing_reservation_and_lease(self, driver):
driver.session.post.side_effect = [
_make_json_response(
{"rows": [{"uuid": "existing-uuid-456", "ip_address": "172.22.8.253"}]}
), # searchReservation — match
_make_json_response({"result": "deleted"}), # delReservation
_make_json_response({"status": "ok"}), # service/reconfigure
_make_json_response({"result": "deleted"}), # delLease
]
driver.session.get.return_value = _make_json_response(
{"rows": [{"address": "172.22.8.253", "hwaddr": "02:aa:bb:cc:dd:ee"}]}
)
result = driver.delete_dhcp_reservation_and_lease(
mac="02:aa:bb:cc:dd:ee", ip="172.22.8.253"
)
assert result == {
"reservation_found": True,
"reservation_deleted": True,
"lease_found": True,
"lease_deleted": True,
}
del_res_call = driver.session.post.call_args_list[1]
assert (
del_res_call.args[0]
== "https://opnsense.example.com/api/kea/dhcpv4/delReservation/existing-uuid-456"
)
reconfigure_call = driver.session.post.call_args_list[2]
assert reconfigure_call.args[0] == "https://opnsense.example.com/api/kea/service/reconfigure"
del_lease_call = driver.session.post.call_args_list[3]
assert del_lease_call.args[0] == "https://opnsense.example.com/api/kea/leases4/delLease"
assert del_lease_call.kwargs["json"] == {"ip-address": "172.22.8.253"}
def test_noop_when_no_reservation_and_no_lease_found(self, driver):
driver.session.post.return_value = _make_json_response({"rows": []})
driver.session.get.return_value = _make_json_response({"rows": []})
result = driver.delete_dhcp_reservation_and_lease(
mac="02:aa:bb:cc:dd:ee", ip="172.22.8.253"
)
assert result == {
"reservation_found": False,
"reservation_deleted": False,
"lease_found": False,
"lease_deleted": False,
}
# Only searchReservation was called — no delReservation/reconfigure/delLease.
assert driver.session.post.call_count == 1
def test_reservation_delete_raises_when_kea_rejects(self, driver):
driver.session.post.side_effect = [
_make_json_response(
{"rows": [{"uuid": "existing-uuid-456", "ip_address": "172.22.8.253"}]}
),
_make_json_response({"result": "not found"}), # delReservation rejected
]
with pytest.raises(RuntimeError, match="Kea rejected reservation delete"):
driver.delete_dhcp_reservation_and_lease(mac="02:aa:bb:cc:dd:ee", ip="172.22.8.253")
# reconfigure must NOT be called after a rejected delete
assert driver.session.post.call_count == 2
def test_lease_delete_failure_is_non_fatal(self, driver):
driver.session.post.side_effect = [
_make_json_response(
{"rows": [{"uuid": "existing-uuid-456", "ip_address": "172.22.8.253"}]}
),
_make_json_response({"result": "deleted"}),
_make_json_response({"status": "ok"}),
]
driver.session.get.side_effect = Exception("connection reset")
result = driver.delete_dhcp_reservation_and_lease(
mac="02:aa:bb:cc:dd:ee", ip="172.22.8.253"
)
assert result["reservation_found"] is True
assert result["reservation_deleted"] is True
assert result["lease_found"] is False
assert result["lease_deleted"] is False
def test_raises_when_kea_plugin_unavailable(self, driver):
driver.session.post.side_effect = Exception("404 Not Found")
with pytest.raises(RuntimeError, match="Kea DHCPv4 plugin unavailable"):
driver.delete_dhcp_reservation_and_lease(mac="02:aa:bb:cc:dd:ee", ip="172.22.8.253")