Expand description
§Draupnir — the nordisk boot / provisioning library
Draupnir is the low-level engine that fires up a runtime instance from a
single BootSpec, across three backends, and drives its power lifecycle:
kvm— a KVM/appliance VM, booted by driving tunnr’stunnr_vm::boot_test(BootSpec) -> BootHandleprimitive (featurebackend-tunnr). Draupnir does not reimplement VM boot — it wires theBoottrait against tunnr.container— an OCI container, brought up over a container runtime.redfish— bare metal, provisioned out-of-band through a BMC’s Redfish REST API (iLO / iDRAC / OpenBMC): insert a virtual-media ISO, set the one-time boot override to that media, power the node on. This is the capability Draupnir uniquely owns.exe— a plain executable run as a child process on this host: the non-VM, non-container shape (theexebuild-thing kind).
§Booting OFF a medium (2026-08-15)
A BootSpec can name a removable medium and a
boot_order, which is what makes an appliance ISO runnable at all:
BootSpec::iso_boot— live-boot an ISO. One spec, routable to the KVM backend as it stands or to real metal withon_metal, soiso-kvmandiso-metalare two runs of one claim rather than two claims.BootSpec::kvm_install_from_medium— boot off the ISO with a blank target disk attached (the INSTALL leg).BootSpec::kvm_boot_installed_disk— boot that disk with the medium absent (the SECOND VM lifetime).validaterefuses a spec that claims to boot the installed system while still holding the medium.
A spec that names no medium and no boot order behaves exactly as it did before these fields existed — both default to “emit nothing”.
§Waiting: a thing that keeps running is the NORMAL case
exe::ExeBoot::await_marker and kvm::KvmBoot::await_serial_marker wait for
a named marker or a deadline, never for the instance to terminate, and both
return the same Seen describing what was actually observed. An appliance that
boots correctly serves forever; waiting for it to exit is waiting for it to fail.
§Where Draupnir sits
Draupnir is the shared low-level boot lib. Two high-level consumers depend on it directly and neither duplicates its boot code:
- jera (edda’s job handler) → depends on Draupnir for job instances
(
process | VM | container). jera stays thin: just job policy. - Skidbladnir (service/systemd, airgap, orchestration) → depends on Draupnir for service instances.
Draupnir itself knows nothing about jobs or services; it just fires up and controls instances. For its KVM backend it calls down into tunnr.
jera ─────────┐
├──▶ Draupnir ──▶ { tunnr (KVM) | OCI runtime | Redfish BMC }
Skidbladnir ──┘§The mythological nod
Draupnir is Odin’s gold ring that drips eight identical copies of itself
every ninth night. Here that is a natural extension, not the core: booting a
fleet of identical machines from one ISO — plan_fleet fans one
BootSpec out into N identical specs (bare-metal fleet provisioning via
Redfish). The core is one library, three boot backends.
Modules§
- container
- Container backend — fire up an OCI container instance (e.g. a redis service) over a container runtime.
- exe
- Exe backend — run a plain executable as a child process on this host.
- kvm
- KVM backend — fire up an appliance VM by driving tunnr.
- redfish
- Redfish backend — provision bare metal out-of-band through a BMC.
- seed
- NoCloud cloud-init seed — draupnir’s owned, pure seed builder
Structs§
- BmcEndpoint
- A BMC (baseboard management controller) endpoint — the out-of-band Redfish service on a bare-metal node (iLO / iDRAC / OpenBMC).
- Boot
Spec - A self-contained boot request. One shape fires up any backend; the
backendselects the driver and [validate] enforces that theimage(and, for Redfish, thebmc) match. - Cloud
Init - cloud-init NoCloud provisioning for a KVM appliance boot: the
user-data(and optionalmeta-data) authored into a small FAT seed image (volume labelcidata) the guest’s cloud-init picks up at first boot. - Fleet
Readback - The fleet-level boot-readback rollup returned by
boot_fleet_and_await: one verdict per member (in fleet ordernode-1,node-2, …) plus the aggregate ready/failed tallies a dispatcher (jera) reads to decide whether the fleet is up. - Machine
- A booted (or booting) instance handle — what a
Boot::bootreturns and whatLifecycleacts on. - Member
Readback - One member’s line in a
FleetReadback: its name paired with its verdict. - PortMap
- A container published-port mapping — a distinct
host:containerpair (podman-p HOST:CONTAINER). This is the general publish form: a service listening on a fixed port inside the container (FalkorDB always binds6379, Spark-Connect15002) can be published on a different host port so several isolated copies coexist on one host — e.g. per-zone offsets (Demoon6379,Teston6380,Prodon6381) that all reach the same in-container6379. Carried onBootSpec::port_maps. - Wait
Options - Knobs for
await_power_state/boot_and_await: how long to wait for the instance to reach the target power state and how often to poll it.Defaultwaits indefinitely (parity withcontainer::RunOptions) and polls every 200 ms — a bounded budget (WaitOptions::bounded) is recommended for a boot readback so a node that never comes up is a timeout, not a hang.
Enums§
- Backend
- Which runtime a
BootSpectargets. - Boot
Order - Which device the firmware is told to boot from — the boot-order half of
the boot-off-a-medium seam (
BootSpec::boot_order). - Boot
Target - The one-time boot device a Redfish node is overridden to.
- Error
- Everything that can go wrong firing up or controlling an instance.
- Image
Source - The bootable payload — the source an instance is fired up from.
- Member
Outcome - The boot-readback verdict for one fleet member in a
boot_fleet_and_awaitrollup — the three ways a member can land. - NetMode
- The container network mode — how the OCI backend attaches the container to
a network. Only the
containerbackend acts on it (KVM/Redfish carry no container network).Defaultis the runtime default (podman/Docker’s own choice — no--networkflag, so the createdHostConfig.network_modestays unset and the create body is byte- identical to a spec that never named a net mode). The airgap case isNone: it renders--network noneand cuts the container off from all egress — the load-bearing wire for Skidbladnir’s airgap container route. - Power
State - The power state of an instance.
- Seen
- What was actually observed while waiting on a running instance — the return
of
exe::ExeBoot::await_markerand ofkvm::KvmBoot::await_serial_marker.
Traits§
- Boot
- Fire up an instance from a
BootSpec. One trait, three implementations (kvm::KvmBoot,container::ContainerBoot,redfish::RedfishBoot). - Lifecycle
- Drive an instance’s power lifecycle after it is fired up.
- Virtual
Media - Redfish virtual-media + boot-override control — the out-of-band steps that
make a bare-metal node boot our ISO. Only the
redfish::RedfishBootbackend implements it; the KVM/container backends have no BMC.
Functions§
- await_
power_ state - Confirm an instance reached a power state — poll a
Lifecycle’sstatusuntil it reportswant, returningOk(())the moment it does. This is the cross-backend boot-status readback seam: aBoot::bootfires an instance up but returns before it has actually powered on (a KVM guest is still booting, a Redfish node is still POSTing, a container is still being scheduled), so a consumer (jera / Skidbladnir) that needs to know the instance is up polls this — the power-lifecycle analogue of the containerrun_to_completiondrive loop, but written against the plainLifecycletrait so it drives any backend (KVM, container, Redfish) and a mock in a unit test with no live instance. - boot
- The unifying entry point — fire up one instance from a
BootSpecacross whichever backend is handed in. Itvalidates the spec first (so a mismatched image/BMC is rejected before any backend is touched), then delegates to the backend’sBoot::boot. The samespecboots the same image on akvm::KvmBoot, acontainer::ContainerBoot, or aredfish::RedfishBoot— one call, three backends. - boot_
and_ await - Boot an instance and confirm it is up — the one-call provision seam jera /
Skidbladnir want:
validate+bootthespeconbackend, thenawait_power_stateit toPowerState::On, returning the liveMachineonly once it has actually powered on. A validation or boot failure short-circuits before any wait (the backend is never touched on an invalid spec —bootenforces that); a boot that never comes up within a boundedWaitOptionsis a timeoutError::Backend. Written againstBoot + Lifecycleso it drives any backend and a mock alike. - boot_
fleet - Drip a fleet from one image —
plan_fleetthe spec intonmembers andbooteach throughbackend, returning a per-member result (a partial fleet is observable: some members may boot while a later one errors). - boot_
fleet_ and_ await - Boot a fleet and roll up who is actually ready — the fleet-level analogue of
boot_and_await, and the multi-machine provision-readback seam jera’s dispatcher wants (jera Roster/WorkPayload → draupnir boots the fleet → this rolls up who came up). Itplan_fleetsspecintonmembers and drives each throughboot_and_await(validate →boot→ confirmPowerState::On), classifying every member’s result into aMemberOutcomeand returning theFleetReadbackrollup (per-member verdict + aggregate ready/failed counts). - boot_
fleet_ and_ await_ parallel - Boot a fleet CONCURRENTLY and roll up who is ready — the parallel sibling of
boot_fleet_and_await. Where the serial call awaits members one-at-a-time (so a fleet’s wall-clock is the sum of the per-member waits), this fans every member onto the gatling fork-join pool (gatling::gatling_forkjoin::gatling_for_each— the constellation’s one sanctioned threading home, ROOT-LAW #0), one worker per member, sonnodes are booted and awaited at the same time and the wall-clock collapses to roughly the slowest member’s wait, not the sum. This is the real win for jera’s multi-machine dispatcher: booting a fleet ofnnodes with a 5-minute per-member budget takes ~5 minutes, not ~5nminutes. - functional_
status - Introspection / emit marker — record one functional-status row for the
nornir test matrix (the constellation-wide introspection-coverage gate).
Wraps
nornir_testmatrix::functional_statusbehind the optionaltestmatrixfeature: ON, it emits a real matrix row nornir reads back; OFF, it is a compiled-out#[inline]no-op with NO nornir dependency, so the lean default build never pulls it.componentis the reporting unit (e.g."draupnir/seed"),checkwhat it verified,okthe verdict,detaila short human note. Mirrors the sibling constellation crates (skidbladnir, ordning-core, korp-collectors). - plan_
fleet - The ring drips eight copies — fan one
BootSpecout intonidentical specs, each with a distinct"{name}-{i}"name (1-based), for booting a fleet of identical machines from one image/ISO. - rollback
- Roll a booted instance back — tear down an instance a boot brought up (or a
half-finished provision) by powering it off through the backend’s
Lifecycle, recording the teardown verdict as a functional-status row. This is the undo seam jera / Skidbladnir call when a provision must be reverted: a boot that never reachedPowerState::On, await_readythat timed out, or a fleet member being reclaimed. GREEN when the backend confirms the power-off; a live backend failure to power it down is a RED row AND a propagatedErr— a rollback that could not complete must be visible, never swallowed. Written against the plainLifecycletrait so it rolls back any backend (KVM / container / Redfish) and a mock in a unit test alike — one shared teardown, never a per-backend twin.
Type Aliases§
- Result
- Draupnir’s result alias.