pxe: reload dnsmasq on host create/delete
CI / Lint + build + test (push) Successful in 1m54s
Release / release (push) Successful in 2m36s

pxe.Supervisor.Reload() was defined but never wired up. After a host
was registered in the UI or via the quick-register JSON endpoint, the
dnsmasq conf still held only the hosts that existed at orchestrator
startup. The new MAC wasn't tagged `known`, so when the host PXE'd,
dnsmasq logged "PXE(eth0) <mac> proxy-ignored" and the boot timed out
back to the BIOS.

Add an optional PXEReloader interface to api.UI, wire it from main
when pxe is enabled, and call u.reloadPXE() after successful Create
and Delete. Logs-and-continues on failure — host registration itself
has already committed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-18 12:31:00 -04:00
parent 157b70f536
commit bce6e08524
2 changed files with 32 additions and 0 deletions
+31
View File
@@ -35,6 +35,34 @@ type UI struct {
Runner *orchestrator.Runner
Tiles *TileEnricher
PublicURL string // user-visible base URL baked into the quick-register one-liner
// PXE, when non-nil, gets Reload()ed after host create/delete so
// dnsmasq's dhcp-host= allowlist reflects the current registry.
// Without this, a newly-registered host PXE-boots and gets
// "proxy-ignored" because its MAC isn't tagged `known`.
PXE PXEReloader
}
// PXEReloader rewrites dnsmasq.conf with the current host list and
// SIGHUPs the subprocess. Satisfied by *pxe.Supervisor.
type PXEReloader interface {
Reload(hosts []model.Host) error
}
// reloadPXE reads the full host list and hands it to the reloader.
// Logs on failure; never returns an error — the HTTP request that
// triggered the host change has already succeeded.
func (u *UI) reloadPXE(ctx context.Context) {
if u.PXE == nil {
return
}
hosts, err := u.Hosts.List(ctx)
if err != nil {
log.Printf("pxe reload: list hosts: %v", err)
return
}
if err := u.PXE.Reload(hosts); err != nil {
log.Printf("pxe reload: %v", err)
}
}
var macRe = regexp.MustCompile(`^[0-9a-f]{2}(:[0-9a-f]{2}){5}$`)
@@ -246,6 +274,7 @@ func (u *UI) CreateHost(w http.ResponseWriter, r *http.Request) {
_ = templates.Registration(form).Render(r.Context(), w)
return
}
u.reloadPXE(r.Context())
http.Redirect(w, r, "/", http.StatusSeeOther)
}
@@ -301,6 +330,7 @@ func (u *UI) CreateHostJSON(w http.ResponseWriter, r *http.Request) {
return
}
log.Printf("api: registered host %d (%s, %s)", id, form.Name, form.MAC)
u.reloadPXE(r.Context())
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
_ = json.NewEncoder(w).Encode(map[string]any{
@@ -456,6 +486,7 @@ func (u *UI) DeleteHost(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
u.reloadPXE(r.Context())
http.Redirect(w, r, "/", http.StatusSeeOther)
}