Report — Settings ▸ Services: dynamic discovery of the split daemons
Synced read-only from
/home/synnet/mirrors/synvirt-product/docs/daemon-split/REPORT-services-panel.md. Edit at the source, not here.
Report — Settings ▸ Services: dynamic discovery of the split daemons
Section titled “Report — Settings ▸ Services: dynamic discovery of the split daemons”Branch: feature/daemon-split @ worktree /home/synvirt/worktrees/daemon-split.
2026-06-10. Base HEAD at session start: 4960e87.
Outcome: green through deploy. The Services panel no longer reads a stale
curated SynVirt list — it now DISCOVERS synvirt-*.{service,timer} units
from systemd at request time. The four ghosts are gone; the split stack
(synvirt-daemon + synvirt-migratord + synvirt-updaterd) plus
network/console/cert-renew/etc. now appear with live status + memory. Only
the daemon binary changed (migratord/updaterd were NOT restarted). Deployed
to .56 → .65 → .75; every protected guest PID is exactly as found.
Commit: 07a93b5 feat(synvirt-services) + this report commit.
Registry before → after
Section titled “Registry before → after”Before (curated const ALLOWLIST)
Section titled “Before (curated const ALLOWLIST)”SynVirt rows were a hardcoded list. Four had no shipped unit file and rendered as permanent “Not installed” ghosts; the real control-plane daemon was absent:
| Curated SynVirt row | Reality |
|---|---|
synvirt-api.service |
never shipped (R4 future rename) → ghost |
synvirt-console-gateway.service |
never shipped as a unit → ghost |
synvirt-storage.service |
R2, never shipped → ghost |
synvirt-host.service |
R3, never shipped → ghost |
synvirt-cert-renew.timer |
real |
synvirt-network.service |
real |
synvirt-daemon.service |
MISSING from the panel |
synvirt-migratord / synvirt-updaterd |
MISSING (didn’t exist when the list was written) |
After (dynamic discovery + curated platform)
Section titled “After (dynamic discovery + curated platform)”- SynVirt rows = discovered.
SystemdClient::discover_synvirt_units()unionsListUnitFiles∪ListUnits, filterssynvirt-*.{service,timer}, dedupes.ServicesService::list()orders them (control-plane daemon first, then alphabetical) and prepends them to the curated platform deps. - Denylist (
SYNVIRT_DENYLIST):synvirt-guest-drain.service(itsExecStopruns the guest drain — a UI Stop would power down running VMs) andsynvirt-self-restart.service(transient oneshot helper). Both are pattern-matched but never surfaced and arenot_managedfor actions. - Platform deps stay curated (libvirtd, virtlogd, chrony, nftables, openvswitch-switch, zfs-zed, ssh, ceph.target@enterprise) so “Not installed” stays a useful signal.
- Self-protection repurposed from the never-shipped
synvirt-api.serviceto the real API daemonsynvirt-daemon.service(is_api_daemon(), also matching the future R4 rename): can’t stop/disable; restart routes through the out-of-process self-restart helper (whose shipped unit already falls back tosynvirt-daemon.service). migratord/updaterd restart SOFT-WARN (never block) via impact warning codes.
- OpenAPI services schema byte-identical. Normalized
--dump-openapi(local build) vs the deployed.65daemon: 9/9/settings/services*paths identical, everyUnitStateView/RestartImpactView/ServicesListView/… schema equal, whole spec normalized-identical. Only the dataset changed. - Suite: full workspace
2039 passed / 0 failed / 4 ignored. - fmt + clippy -D clean on touched code. (Pre-existing crate fmt
deviations in untouched
error.rs/journal.rs/self_restart.rsleft as found.) - Tests added/updated: discovery/merge, ordering (daemon-first + alphabetical + dedupe), denylist suppression, edition gate, api-daemon protection, discovered-unit action set — all against a mocked systemd source.
Per-host deploy (only the daemon binary changed)
Section titled “Per-host deploy (only the daemon binary changed)”Procedure per host: PID snapshot → timestamped backup
synvirt-daemon.bak-$(date +%Y%m%dT%H%M) (NOT overwriting last night’s
bak-20260610) → install binary (rename-over-running, safe) +
systemctl daemon-reload → systemctl restart synvirt-daemon (plain
restart; sled-owning migratord and updaterd untouched) → PID gate → smokes.
| Host | Pre ver | Post ver | units (daemon/migratord/updaterd) | ghosts | discovered rows | Status |
|---|---|---|---|---|---|---|
| 10.10.26.56 | Fix75+build.1024 | Fix75+build.1027 | active / active / active | NONE | 11 synvirt + 7 platform = 18 | ✅ |
| 10.10.26.65 | Fix75+build.1024 | Fix75+build.1027 | active / active / active | NONE | 13 synvirt + 7 platform = 20 | ✅ |
| 10.10.26.75 | Fix75+build.1024 | Fix75+build.1027 | active / active / active | NONE | 10 synvirt + 7 platform = 17 | ✅ |
migratord/updaterd were NEVER restarted — only synvirt-daemon. Each host’s
row count differs because discovery reflects that host’s actual units (e.g.
.65 also has synvirt-live + synvirt-vnic-rename).
SEV-0 guest PID continuity (before / after every daemon restart)
Section titled “SEV-0 guest PID continuity (before / after every daemon restart)”| Host | Guest | PID before | PID after | start time | Verdict |
|---|---|---|---|---|---|
| 10.10.26.56 | — (none) | — | — | — | n/a |
| 10.10.26.65 | Windows Server 2026 | 1699966 | 1699966 | Tue Jun 9 14:39:58 2026 (unchanged) | ✅ PRESERVED |
| 10.10.26.75 | Windows Server 2016 | 1469944 | 1469944 | Sun Jun 7 13:51:37 2026 (unchanged) | ✅ PRESERVED |
Restart is guest-safe by design: the deployed daemon is Fix75 (post-Fix70 ⇒
systemctl restart is HTTP-only) and a hard PID gate ran immediately after
each restart with an auto-rollback to the timestamped backup had the PID
changed. It passed on both guest hosts. End-of-session snapshot reconfirmed
all PIDs.
Smoke evidence (per host)
Section titled “Smoke evidence (per host)”GET /api/v1/settings/services→ daemon+migratord+updaterd present,active, withmemory_current_bytes> 0; the four ghosts absent; guest-drain + self-restart absent; 7 platform rows present.- Live per-host JSON saved as evidence:
docs/daemon-split/hosts/10.10.26.{56,65,75}-services.json. GET /api/v1/migrator/migrationsthrough the proxy → alive (.56/.65 = 0 jobs, .75 = 7 historical jobs incl. last session’s cancelled orphan).journalctl -p errfor the three units after each restart → no entries (.75 shows only pre-existing, unrelated OVSvSwitch0-bondnoise).
synvirt-network “Activating” finding (.65 — investigate-only, NOT touched)
Section titled “synvirt-network “Activating” finding (.65 — investigate-only, NOT touched)”The dashboard showed synvirt-network stuck “Activating” on .65. Root
cause, from systemctl status synvirt-network:
synvirt-network.service: Scheduled restart job, restart counter is at 47538.(install)[…]: synvirt-network.service: Failed to set up mount namespacing: /etc/systemd/resolved.conf.d: No such file or directoryActive: activating (start-pre)The unit’s mount-namespacing (a ReadWritePaths=/BindPaths=-style
directive) references /etc/systemd/resolved.conf.d, which does not exist on
.65. systemd cannot set up the namespace ⇒ ExecStartPre fails ⇒
Restart= loops (counter 47538 — long-standing, pre-dates this work).
That perpetual activating (start-pre) is exactly what the panel now
surfaces (active_state=activating, sub_state=auto-restart).
This is a pre-existing host bug, not caused by this change — and the dynamic panel is now correctly exposing it (the curated list showed synvirt-network too, but the value here is the operator can see it is crash-looping). The unit was deliberately NOT touched.
Suggested fix for Tony (your call, not done here): either
mkdir -p /etc/systemd/resolved.conf.d on .65, or make the unit tolerate
the missing dir / create it in an earlier ExecStartPre. synvirt-network
on .56 is inactive and on .75 inactive — only .65 is in the crash-loop.
Left for Tony
Section titled “Left for Tony”- Open the dashboard on each host → Settings ▸ Services. Confirm the split daemons render with memory, the four ghosts are gone, and the platform deps still show. The UI needs zero changes (OpenAPI byte-identical).
- synvirt-network on .65 — apply the resolved.conf.d fix above to stop the 47k-restart loop.
- Action policy is live but unexercised by a click: discovered synvirt units get synvirt-network’s action set; synvirt-daemon is stop/disable locked with restart via the self-restart helper; migratord/updaterd restart soft-warns. Worth a manual click-test of “restart synvirt-daemon” from the UI (it should reconnect, not brick).
- Discovery surfaces ALL real synvirt units (console, boot-splash,
cpufreq, tuned, debug-cleanup, vnic-rename, cert-renew.service, …), not
just the long-running daemons. That is the intended zero-curation
behavior; if any should be hidden, extend
SYNVIRT_DENYLIST. - Branch still unmerged/unpushed — your call.