Skip to main content

Crate draupnir

Crate draupnir 

Source
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’s tunnr_vm::boot_test(BootSpec) -> BootHandle primitive (feature backend-tunnr). Draupnir does not reimplement VM boot — it wires the Boot trait against tunnr.
  • container — an OCI container, brought up over a container runtime.
  • redfishbare 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 (the exe build-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 with on_metal, so iso-kvm and iso-metal are 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). validate refuses 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 ISOplan_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).
BootSpec
A self-contained boot request. One shape fires up any backend; the backend selects the driver and [validate] enforces that the image (and, for Redfish, the bmc) match.
CloudInit
cloud-init NoCloud provisioning for a KVM appliance boot: the user-data (and optional meta-data) authored into a small FAT seed image (volume label cidata) the guest’s cloud-init picks up at first boot.
FleetReadback
The fleet-level boot-readback rollup returned by boot_fleet_and_await: one verdict per member (in fleet order node-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::boot returns and what Lifecycle acts on.
MemberReadback
One member’s line in a FleetReadback: its name paired with its verdict.
PortMap
A container published-port mapping — a distinct host:container pair (podman -p HOST:CONTAINER). This is the general publish form: a service listening on a fixed port inside the container (FalkorDB always binds 6379, Spark-Connect 15002) can be published on a different host port so several isolated copies coexist on one host — e.g. per-zone offsets (Demo on 6379, Test on 6380, Prod on 6381) that all reach the same in-container 6379. Carried on BootSpec::port_maps.
WaitOptions
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. Default waits indefinitely (parity with container::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 BootSpec targets.
BootOrder
Which device the firmware is told to boot from — the boot-order half of the boot-off-a-medium seam (BootSpec::boot_order).
BootTarget
The one-time boot device a Redfish node is overridden to.
Error
Everything that can go wrong firing up or controlling an instance.
ImageSource
The bootable payload — the source an instance is fired up from.
MemberOutcome
The boot-readback verdict for one fleet member in a boot_fleet_and_await rollup — the three ways a member can land.
NetMode
The container network mode — how the OCI backend attaches the container to a network. Only the container backend acts on it (KVM/Redfish carry no container network). Default is the runtime default (podman/Docker’s own choice — no --network flag, so the created HostConfig.network_mode stays unset and the create body is byte- identical to a spec that never named a net mode). The airgap case is None: it renders --network none and cuts the container off from all egress — the load-bearing wire for Skidbladnir’s airgap container route.
PowerState
The power state of an instance.
Seen
What was actually observed while waiting on a running instance — the return of exe::ExeBoot::await_marker and of kvm::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.
VirtualMedia
Redfish virtual-media + boot-override control — the out-of-band steps that make a bare-metal node boot our ISO. Only the redfish::RedfishBoot backend implements it; the KVM/container backends have no BMC.

Functions§

await_power_state
Confirm an instance reached a power state — poll a Lifecycle’s status until it reports want, returning Ok(()) the moment it does. This is the cross-backend boot-status readback seam: a Boot::boot fires 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 container run_to_completion drive loop, but written against the plain Lifecycle trait 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 BootSpec across whichever backend is handed in. It validates the spec first (so a mismatched image/BMC is rejected before any backend is touched), then delegates to the backend’s Boot::boot. The same spec boots the same image on a kvm::KvmBoot, a container::ContainerBoot, or a redfish::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 + boot the spec on backend, then await_power_state it to PowerState::On, returning the live Machine only 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 — boot enforces that); a boot that never comes up within a bounded WaitOptions is a timeout Error::Backend. Written against Boot + Lifecycle so it drives any backend and a mock alike.
boot_fleet
Drip a fleet from one imageplan_fleet the spec into n members and boot each through backend, 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). It plan_fleets spec into n members and drives each through boot_and_await (validate → boot → confirm PowerState::On), classifying every member’s result into a MemberOutcome and returning the FleetReadback rollup (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, so n nodes 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 of n nodes with a 5-minute per-member budget takes ~5 minutes, not ~5n minutes.
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_status behind the optional testmatrix feature: 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. component is the reporting unit (e.g. "draupnir/seed"), check what it verified, ok the verdict, detail a short human note. Mirrors the sibling constellation crates (skidbladnir, ordning-core, korp-collectors).
plan_fleet
The ring drips eight copies — fan one BootSpec out into n identical 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 reached PowerState::On, a wait_ready that 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 propagated Err — a rollback that could not complete must be visible, never swallowed. Written against the plain Lifecycle trait 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.