- TypeScript 95.5%
- Svelte 4.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Core deprecated running host work inside a route handler (core#21): the module
declares an action, the host owns the run's state, ordering and clock.
/refresh and /sel/clear both force a full re-collect, and a SEL elist is ~1.3s
of ipmitool. Until now either could land on top of the collector's own
load(true) with nothing arbitrating. They now share `lock: () => "ipmi"`, so
the BMC has one writer at a time, and onSelfConflict "drop" says the honest
thing about a second forced re-read: it is the same re-read.
Clearing the SEL is destructive and args-free, which is the case for
`console: true` — wiping the event log should leave a row naming who did it,
and Activity is now able to offer the button as well as record it.
Both routes are bound to schema actions and the Android app has no notion of a
run yet, so they start the run and wait for its verdict rather than answering
{ runId }. The 503 carrying `error`/`hint` is what puts the BMC's complaint
next to the button; `hint` keeps its own key because it is the actionable half
("pass /dev/ipmi0 into the container"). `runId` rides along for the web shell,
which follows the run to show its steps.
/identify stays a plain route. It is one ipmitool call that returns
immediately and changes nothing but a blinking LED; a run row per LED blink is
noise, and the deprecation is about work long enough to watch.
`expose` names result only — sensor counts are structure, while state.error
and its hint name the exec strategy and the device path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
| backend | ||
| frontend | ||
| CLAUDE.md | ||
| opsdeck.module.json | ||
| README.md | ||
OpsDeck IPMI module
The baseboard management controller's view of the machine, as an OpsDeck module: fans, power supplies, voltage rails, inlet and DIMM temperatures, chassis intrusion, power draw and the System Event Log.
Why this is not part of the hardware module
Almost none of this is reachable through Linux. On a server board, hwmon
typically exposes the CPU die, the NIC and a GPU and nothing else — a
Supermicro H12SSL-i reports zero fan*_input, zero voltage rails, zero
PSU status and no inlet temperature. A fan failing on such a machine is
invisible to lm-sensors right up to the point where the CPU throttles.
The BMC has all of it, plus two things nothing else has:
- The System Event Log — correctable and uncorrectable ECC errors, thermal
trips,
CATERR, PSU failures and AC loss, chassis intrusion, all recorded with timestamps by a processor that keeps running when the OS does not. Correctable ECC counts are the earliest warning a DIMM gives. - Power draw in watts (DCMI), measured rather than estimated.
Keeping it separate also keeps hardware generic: most machines have no BMC,
and this module needs a device passed into the container.
Requirements
The host needs ipmitool and a BMC:
apt-get install ipmitool
modprobe ipmi_devintf # plus ipmi_si; usually automatic
ls -l /dev/ipmi0 # crw------- root root
The OpsDeck container needs that device. /dev/ipmi0 is root-owned 0600
and Docker's device cgroup denies char devices that were not passed in, so
this is required no matter how the binary is reached:
services:
opsdeck:
devices:
- /dev/zfs
- /dev/ipmi0 # <- IPMI
Commands run through ctx.host (chroot into the host-root bind mount), so the
host's ipmitool is used, with whatever vendor OEM support its version has.
The module needs no extra capabilities and does not run privileged. When
something is missing it says which thing and what to change — see the
Status field on the module page.
Install it:
OPSDECK_EXTERNAL_MODULES=https://git.example.com/you/module_ipmi.git#<sha>
What it shows
Module page
| Headline | overall sensor health, power draw, hottest sensor, cooling summary |
| Charts | temperatures, fan speeds and power draw over the last 2 h |
| Chassis | system power, PSU presence and failure, power/cooling/drive faults, intrusion, power restore policy |
| Sensors | every SDR entry with its reading, the BMC's own verdict and the threshold it is judged against |
| BMC | controller vendor and firmware, event-log usage, last read, host access |
Event log page — the SEL, newest first, each record classified
critical / warning / info, with an admin-only clear.
Dashboard card — power draw, hottest sensor, and one line each for cooling, power supplies and the event log.
Metrics: ipmi.temp, ipmi.fan, ipmi.volt (tagged per sensor),
ipmi.power, ipmi.sensors.warn, ipmi.sensors.crit.
Alerts
Notifications are edge-triggered — a machine with two unpopulated fan headers has them all year, and announcing that every minute is how people learn to dismiss the one alert that matters. The first tick after a restart only establishes the baseline.
- a sensor crossing into the BMC's warning or critical range
- a sensor that had a reading and stops having one (the fan-died case; headers that were never populated stay quiet)
- a power supply reporting failure, AC loss or absence
- new SEL records above
info - chassis intrusion, cooling fault, drive fault, power fault
- the BMC becoming unreachable (once per distinct failure)
Configuration
| variable | default | |
|---|---|---|
OPSDECK_MOD_IPMI_INTERVAL_SEC |
60 |
poll interval, clamped to 15–3600 |
OPSDECK_MOD_IPMI_IDENTIFY_SECONDS |
15 |
how long the identify LED blinks |
A poll is one sdr elist (~1.3 s) plus three cheap commands. The event log is
only re-read when sel info says it changed, and the sensor thresholds are
read once because they are static.
Deliberately not here
Power control. ipmitool chassis power cycle issued from a web UI running
on the machine being cycled kills the thing serving the button, and the only
situation where it helps — a wedged host — is the situation where OpsDeck is
down too. Doing it properly means talking to the BMC out-of-band from a
different machine, which is a different deployment, not a button. The identify
LED is here because it is the useful half with none of the risk.
IPMI over LAN. -I lanplus would let one OpsDeck watch other machines'
BMCs, but every way ipmitool accepts a password from this module's position
puts it in a process argument list, and /proc/*/cmdline is world-readable.
Half-safe credential handling is worse than none, so this module is in-band
only.
Development
No standalone build — the module builds inside an OpsDeck core checkout:
cp -r . <core>/packages/modules/ipmi
cd <core>
deno check packages/modules/ipmi/backend/mod.ts
deno test --allow-read packages/modules/ipmi/backend/ipmitool_test.ts
deno task build:modules
OPSDECK_AUTH=disabled OPSDECK_FAKE_DATA=1 OPSDECK_MODULES=ipmi deno task start
# http://localhost:8080/m/ipmi
OPSDECK_FAKE_DATA=1 runs a synthetic BMC — it fabricates ipmitool's text
and feeds it to the real parsers, so the fake path exercises the same code the
real one does. Remove the copy from the core checkout afterwards; this repo is
the source of truth.
Against real hardware without deploying, the commands the module runs are:
ipmitool sdr elist
TZ=UTC ipmitool sel elist
ipmitool sensor # thresholds
ipmitool chassis status
ipmitool dcmi power reading
TZ=UTC is not cosmetic: the BMC's clock, the host's zone and the container's
zone are three different opinions, and timestamps are only converted to epoch
milliseconds when ipmitool printed them as UTC.