A small CLI tool to discover bootable node NICs via BMC Redfish and produce a YAML inventory (bmcs[] and nodes[]).
This repository contains a lightweight Go implementation originally based on small scripts for creating an initial BMC inventory and then discovering node NICs through Redfish.
- Generate an initial
inventory.yamlwith abmcslist (xname, MAC, IP) using--init-bmcs. - Discover bootable NICs via Redfish on each BMC and allocate IPs from a given subnet.
- Trigger firmware updates via Redfish UpdateService SimpleUpdate.
- Output file format: a single YAML file with two top-level keys:
bmcs: list of management controllers (xname, mac, ip)nodes: list of discovered node network records (xname, mac, ip)
main.go— minimal entrypoint that invokes the Cobra CLI.cmd/— Cobra commands:init-bmcs— generate initial inventory with BMC entriesdiscover— discover bootable NICs via Redfish and update nodes[]firmware— trigger firmware updates (BMC/BIOS) via SimpleUpdate
internal/— code split by concern:inventory/— YAML types (Entry,FileFormat)redfish/— minimal Redfish client and bootable NIC heuristicsnetalloc/— IP allocation usinggithub.com/metal-stack/go-ipamxname/— xname helpers and conversionsinitbmcs/— helpers used by theinit-bmcscommanddiscover/— discovery orchestration (Redfish + IP allocation)
examples/— sample files (e.g.,inventory.yaml).
This project uses Go modules. From the repo root:
# fetch modules and build
go mod tidy
go build -o ochami_bootstrap .Show help:
./ochami_bootstrap --help./ochami_bootstrap init-bmcs --file examples/inventory.yaml \
--chassis "x9000c1=02:23:28:01,x9000c3=02:23:28:03" \
--bmc-subnet 192.168.100.0/24 \
--nodes-per-chassis 32 \
--nodes-per-bmc 2 \
--start-nid 1Writes examples/inventory.yaml with a bmcs: list and nodes: [].
The discovery flow reads the YAML --file (must contain non-empty bmcs[]) and writes back the same file with updated nodes[].
Required env vars:
REDFISH_USER— Redfish usernameREDFISH_PASSWORD— Redfish password
Example (same subnet for BMCs and nodes):
export REDFISH_USER=admin
export REDFISH_PASSWORD=secret
./ochami_bootstrap discover \
--file examples/inventory.yaml \
--node-subnet 10.42.0.0/24 \
--timeout 12s \
--insecure \
--ssh-pubkey ~/.ssh/id_rsa.pub # optional: set AuthorizedKeys on each BMCExample (separate subnets for BMCs and nodes):
export REDFISH_USER=admin
export REDFISH_PASSWORD=secret
./ochami_bootstrap discover \
--file examples/inventory.yaml \
--bmc-subnet 192.168.100.0/24 \
--node-subnet 10.42.0.0/24 \
--timeout 12s \
--insecure \
--ssh-pubkey ~/.ssh/id_rsa.pub # optional: set AuthorizedKeys on each BMCNotes:
- The program makes simple heuristic decisions about which NIC is bootable (UEFI path hints, DHCP addresses, or a MAC on an enabled interface).
- IP allocation is done with
github.com/metal-stack/go-ipam. The code reserves.1(first host) as a gateway and avoids network/broadcast implicitly. - You can specify
--bmc-subnetand--node-subnetseparately. If only one is provided, it will be used for both BMCs and nodes. - If
--ssh-pubkeyis provided, the tool attempts a Redfish PATCH to/redfish/v1/Managers/BMC/NetworkProtocolwith an OEM payload settingSSHAdmin.AuthorizedKeysto the contents of the file.
Use the firmware subcommand to invoke Redfish UpdateService SimpleUpdate on targets. You can specify either a preset --type (cc|nc|bios) or provide explicit --targets URIs.
Required env vars:
REDFISH_USER— Redfish usernameREDFISH_PASSWORD— Redfish password
Examples:
# Update BMC firmware on all hosts in inventory.yaml
export REDFISH_USER=admin
export REDFISH_PASSWORD=secret
./ochami_bootstrap firmware \
--file examples/inventory.yaml \
--type cc \
--image-uri http://10.0.0.1/images/bmc-firmware.bin \
--protocol HTTP \
--timeout 5m
# Update BIOS firmware using explicit targets (example Node0/Node1 BIOS paths)
./ochami_bootstrap firmware \
--hosts 10.1.1.20,10.1.1.21 \
--targets /redfish/v1/UpdateService/FirmwareInventory/Node0.BIOS,/redfish/v1/UpdateService/FirmwareInventory/Node1.BIOS \
--image-uri http://10.0.0.1/images/bios.cap \
--protocol HTTP
# Update BMC firmware on many hosts in parallel (batch size of 10)
./ochami_bootstrap firmware \
--file examples/inventory.yaml \
--type bmc \
--image-uri http://10.0.0.1/images/bmc-firmware.bin \
--protocol HTTP \
--batch-size 10
# Skip update if already at expected version
./ochami_bootstrap firmware \
--file examples/inventory.yaml \
--type bmc \
--image-uri http://10.0.0.1/images/bmc-firmware.bin \
--expected-version "nc.1.9.8" \
--protocol HTTPNotes:
- Preset
--typevalues:ccorbmc: targets BMC firmware (/redfish/v1/UpdateService/FirmwareInventory/BMC).nc: same as BMC for now (adjust if your platform exposes a different target).bios: uses two targets (Node0.BIOS,Node1.BIOS) by default; use--targetsif your platform differs.
- You can provide
--hosts(comma-separated hostnames/IPs) to override reading from--file. --insecureallows skipping TLS verification for BMC HTTPS endpoints.--batch-sizeenables parallel firmware updates. Default is 0 (serial). Set to number of concurrent updates desired (e.g., 10).--expected-versionchecks current firmware version before updating. Skips update if already at expected version.--forceoverrides version checking and forces the update even if already at expected version.
You can query inventory BMCs to get a quick summary of firmware versions and which hosts are currently updating.
Usage:
export REDFISH_USER=admin
export REDFISH_PASSWORD=secret
./ochami_bootstrap firmware status --file examples/inventory.yaml --batch-size 10What it reports:
- Total hosts scanned
- Count of hosts currently "in-progress" (based on UpdateService/FirmwareInventory state and status conditions)
- Counts grouped by firmware
Version - Per-host errors if any
Notes:
- Uses the same
--file,--hosts,--targets,--timeout,--insecure, and--batch-sizeflags as thefirmwaresubcommand. - The detection heuristic inspects
FirmwareInventoryStateandConditionsto infer in-progress updates; it does not queryTaskServiceby default. - To continuously monitor updates, re-run this command periodically or use a watch/TUI mode (to be added).
- Global
--debugprints Redfish request methods and paths, plus response status codes, to stderr. No credentials are logged. - Use
--dry-runto plan actions without contacting hardware:discover --dry-runlists BMCs that would be contacted, the subnet to use, and the output file; it does not patch SSH keys, discover NICs, or write files.firmware --dry-runprints the SimpleUpdate action per host (image URI, targets, protocol) without posting.
Example:
./ochami_bootstrap --debug discover --file examples/inventory.yaml --node-subnet 10.42.0.0/24 --dry-run
./ochami_bootstrap --debug firmware --file examples/inventory.yaml --type cc --image-uri http://10.0.0.1/bmc.bin --dry-runIf a Redfish call fails, errors include the HTTP status and the body returned by the BMC where available to aid troubleshooting.
- Go (module aware). The project will download dependencies with
go mod tidy. github.com/metal-stack/go-ipam— used for IP allocation.gopkg.in/yaml.v3— YAML parsing and writing.
- Add unit tests for the xname / MAC generation helpers and the Redfish parsing heuristics.
- Add input validation for chassis/macro formats if you require stricter MAC formatting.
- Consider adding a
--dry-runmode for discovery to avoid writing changes while testing.
Pick an appropriate license for your project. This repo currently has none specified.
If you'd like, I can also add a small README section that includes an example inventory.yaml and a quick test script to validate the discovery flow without real BMCs (e.g., a mock HTTP server).