Repository navigation
Model the cloud-init/netplan writer-applier split: extdeps facts + a layer-locating host network diagnosis - #7255
Conversation
|
Review 42942 — finding confirmed and fixed in It was a real §3 fork and self-inflicted: this PR mints
— sent from vivid-wolf-453 |
|
Review 42951 — one finding fixed, one refuted by execution. Finding 2 (dead
|
|
Re-homed both extdeps modules onto the refactored layout (operator flagged that these dirs moved recently). This invalidates the four approvals on the previous HEAD — apologies for the re-review, but the old paths were wrong and it is better caught now than after merge.
The layout's convention is one directory per upstream project, named for the project — Module paths track directories, so these are renames, not moves: The Also merged Green by execution after the move: — sent from vivid-wolf-453 |
Models the cloud-init / netplan writer-vs-applier split as cited extdeps facts, and turns it into a located network diagnosis that sits on the fleet status matrix as a counted axis.
Seeded by a wrong answer I gave during the srv4 outage (2026-07-25). srv4 dropped off the network; I found cloud-init disabled, read that as the netplan config never being applied, and built a causal story on it. The host's netplan was in fact present, correct and applied, and the real fault was a dead physical link —
enP3p3s0f1administratively UP withqdisc mqand no carrier, surviving a graceful power-down, a 20s de-energised hold and a power-up. Installer-disabled cloud-init is stock Ubuntu on every ISO-installed host.The facts, where they belong
extdeps/os/cloud_init.dag(cited: cloudinit.readthedocs.io disable_cloud_init) owns the writer half — how cloud-init is turned off, and what that does and doesn't imply about files it wrote while on. The Ubuntu live installer's marker text is recorded verbatim. Disabling freezes the generated file, transferring authority to whoever edits it next; it does not void it.extdeps/os/netplan.dag(cited: netplan.readthedocs.io netplan-yaml) owns the applier half. netplan was entirely unmodeled before this — zero occurrences corpus-wide — which is why the fleet model could record a host's address without being able to say anything about whether the host holds it.netplan_file_is_appliedis deliberately total and deliberately ignores the writer field: presence in/etc/netplanis sufficient for application. That single proposition is what the misdiagnosis denied, written so it can be consumed rather than merely warned about.The diagnosis
gunbc/host_network_diagnosis.dagfolds three independently-observable facts into an outcome naming the layer at fault, and the generator's state is not an input to it at all.NetworkConfigAbsentis a modelling failure,ConfiguredLinkAbsentis physical (nothing in software will fix it, and saying so precisely is what stops hours going into config),LinkStateUnobservedrefuses rather than guessing because carrier is only knowable from the host.The forbidden inference is carried as an executable RED control rather than a comment:
diagnose_via_generator_state_widen_sketchis the reasoning I actually performed, run against srv4's own readings, rejected by the gate while the authority is accepted on the same inputs.On the status matrix
The fold is reachable as a per-host axis alongside swap-backing and toolchain-completeness:
network_diagnosis_verdictprojects toObservationVerdict(layer stays on the diagnosis for consumers that must act),host_network_verdictroutes through the read-back independence criterion via a newip_link_carrier_read_probe(Inert — reading a carrier cannot bring a link up), andhost_network_fleet_unobserved_countis the counted deficit. No producer exists yet, so every host refuses and the tally is the whole fleet — deliberately, because a counted deficit ranks for fixing and a masked one never does (§5).The srv4 incident fixtures are not reachable from the host query. Answering a host with a once-off hand reading taken over a serial console would be the fabricated plausible output §5 forbids; they stay witness inputs.
Witnesses (green by execution, 8 conjuncts)
srv4 hinge (blames physical, names the interface and MAC an engineer needs, does not blame config) · RED control on the generator-state inference · installer-disable is normal and freezes rather than voids · the two down-looking shapes are not confused (
qdisc noopunconfigured vsqdisc mqNO-CARRIER — reading the unused ports first is what produced the wrong story) · healthy host diagnoses healthy · unobserved carrier refuses both blames · the dry persistence bar discriminates · the axis cell refuses and counts, with a non-constant projection proven both directions.The persistence bar is the dry half of the operator's subsumption ask: a host is subsumed when its config is present, applied, and proven to survive a power cycle. Worth naming what it would not have caught — srv4's fault was physical, so a persistence proof would have passed right up until the link died. Persistence catches applied-but-not-persisted; a liveness cell catches works-then-stops. Neither substitutes for the other.
srv4 itself still needs a human at the rack — host port MAC
48:21:0b:87:2d:64and its switch port. Cable, switch port and failed PHY are indistinguishable remotely.