Summary
When creating a new reservation via the UI, check whether the IP address the user is typing already exists in NetBox IPAM. Surface the result as an inline advisory on the form — no blocking, just visibility.
Motivation
Currently the form gives no feedback about the NetBox state of an IP until after the reservation is saved and optionally synced. If the IP already exists in NetBox and was not created by Kea sync (e.g. it is a manually maintained entry assigned to a device interface), the user has no way to know this until sync silently overwrites it.
Proposed behaviour
On blur of the IP address field (DHCPv4: ip_address, DHCPv6: ip_addresses), fire a lightweight AJAX call to a new server-scoped endpoint. The endpoint queries NetBox and returns a small HTML fragment injected below the field:
| NetBox state |
Fragment |
| IP not in NetBox |
(empty — no output) |
IP exists, description starts with "Synced from Kea DHCP" or is blank |
Blue info: "Already in NetBox IPAM (status: reserved). Saving will update it." |
| IP exists, foreign description or assigned to an interface |
Yellow warning: "This IP exists in NetBox and was not created by Kea sync (status: active, description: 'Router loopback', assigned to: Router-1/eth0). Syncing will overwrite this entry." |
The check is advisory only — the reservation can still be saved regardless. It only runs in Add mode; in Edit mode the IP field is already disabled and cannot change.
Implementation plan
1. New view — ReservationCheckNetboxIPView (sync_views.py)
GET /plugins/netbox-kea/servers/<pk>/reservation/check-ip/?ip=<addr>
- Server-scoped via
pk so existing Server permission checking applies.
- Validates
ip param with netaddr.IPAddress; returns HttpResponse("") on invalid/missing.
- Calls
sync.get_netbox_ip(ip_str) to look up the NetBox entry.
- Determines ownership:
description.startswith("Synced from Kea DHCP") or blank description → Kea-managed; anything else → foreign.
- Returns rendered
netbox_kea/inc/reservation_ip_check.html fragment.
2. New fragment template — netbox_kea/inc/reservation_ip_check.html
Three states described in the table above. Shows IP address as a link, status badge, description, and assigned object when present.
3. URL registration — urls.py
path(
"servers/<int:pk>/reservation/check-ip/",
views.ReservationCheckNetboxIPView.as_view(),
name="reservation_check_ip",
),
4. Template change — server_reservation_form.html
Add a target div after {% render_form form %}:
<div id="netbox-ip-check"></div>
Add an inline <script> block (plain fetch, no HTMX dependency) that:
- Skips attachment when
action != "Add".
- Selects
#id_ip_address (v4) or #id_ip_addresses (v6) based on dhcp_version.
- On
blur, takes the first comma-separated IP (v6 may have multiple), fetches the endpoint, swaps the fragment into #netbox-ip-check.
- Clears the div when the field is empty.
What does NOT change
sync.py — no changes to sync functions or ownership logic.
- Form fields — no new form fields, no widget attribute changes.
- POST behaviour — saving the reservation and syncing to NetBox work exactly as today; this is purely a pre-submission hint.
Summary
When creating a new reservation via the UI, check whether the IP address the user is typing already exists in NetBox IPAM. Surface the result as an inline advisory on the form — no blocking, just visibility.
Motivation
Currently the form gives no feedback about the NetBox state of an IP until after the reservation is saved and optionally synced. If the IP already exists in NetBox and was not created by Kea sync (e.g. it is a manually maintained entry assigned to a device interface), the user has no way to know this until sync silently overwrites it.
Proposed behaviour
On
blurof the IP address field (DHCPv4:ip_address, DHCPv6:ip_addresses), fire a lightweight AJAX call to a new server-scoped endpoint. The endpoint queries NetBox and returns a small HTML fragment injected below the field:"Synced from Kea DHCP"or is blankThe check is advisory only — the reservation can still be saved regardless. It only runs in Add mode; in Edit mode the IP field is already disabled and cannot change.
Implementation plan
1. New view —
ReservationCheckNetboxIPView(sync_views.py)GET /plugins/netbox-kea/servers/<pk>/reservation/check-ip/?ip=<addr>pkso existingServerpermission checking applies.ipparam withnetaddr.IPAddress; returnsHttpResponse("")on invalid/missing.sync.get_netbox_ip(ip_str)to look up the NetBox entry.description.startswith("Synced from Kea DHCP")or blank description → Kea-managed; anything else → foreign.netbox_kea/inc/reservation_ip_check.htmlfragment.2. New fragment template —
netbox_kea/inc/reservation_ip_check.htmlThree states described in the table above. Shows IP address as a link, status badge, description, and assigned object when present.
3. URL registration —
urls.py4. Template change —
server_reservation_form.htmlAdd a target div after
{% render_form form %}:Add an inline
<script>block (plainfetch, no HTMX dependency) that:action != "Add".#id_ip_address(v4) or#id_ip_addresses(v6) based ondhcp_version.blur, takes the first comma-separated IP (v6 may have multiple), fetches the endpoint, swaps the fragment into#netbox-ip-check.What does NOT change
sync.py— no changes to sync functions or ownership logic.