- enclosure: fetch SES element descriptor page (0x07) and label temp/fan/ psu/voltage elements with human-readable names - temps service + /api/temps: per-enclosure hotspot (max across SES sensors and housed drive temps) plus named SES sensor list - mqtt_publisher: paho-mqtt with HA discovery (device per enclosure, hotspot + per-sensor entities), LWT availability, opt-in via MQTT_HOST - secrets: OpenBao KV v2 reader; MQTT creds sourced from secret/home_assistant with env fallback - compose/requirements/README updated
108 lines
4.2 KiB
Markdown
108 lines
4.2 KiB
Markdown
# JBOD Monitor
|
|
|
|
REST API for monitoring drive health in JBOD enclosures on Linux.
|
|
|
|
Auto-discovers SES enclosures via sysfs, maps drives to physical slots, and exposes SMART health data.
|
|
|
|
## Prerequisites
|
|
|
|
- Linux with SAS/SATA JBODs connected via HBA
|
|
- `smartmontools` — for `smartctl` (SMART data)
|
|
- `sg3-utils` — for `sg_ses` (SES enclosure data)
|
|
- Python 3.11+
|
|
|
|
```bash
|
|
# Debian/Ubuntu
|
|
apt install smartmontools sg3-utils
|
|
|
|
# RHEL/Fedora
|
|
dnf install smartmontools sg3_utils
|
|
```
|
|
|
|
## Install
|
|
|
|
```bash
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
## Run
|
|
|
|
The API needs root access for `smartctl` to query drives:
|
|
|
|
```bash
|
|
sudo uvicorn main:app --host 0.0.0.0 --port 8000
|
|
```
|
|
|
|
## API Endpoints
|
|
|
|
| Endpoint | Description |
|
|
|---|---|
|
|
| `GET /api/health` | Service health + tool availability |
|
|
| `GET /api/enclosures` | List all discovered SES enclosures |
|
|
| `GET /api/enclosures/{id}/drives` | List drive slots for an enclosure |
|
|
| `GET /api/drives/{device}` | SMART detail for a block device |
|
|
| `GET /api/overview` | Aggregate enclosure + drive health |
|
|
| `GET /api/temps` | Per-enclosure temperatures: named SES sensors + hotspot |
|
|
| `GET /docs` | Interactive API docs (Swagger UI) |
|
|
|
|
## Home Assistant (MQTT)
|
|
|
|
The monitor can publish per-enclosure temperatures to Home Assistant over MQTT
|
|
using [HA discovery](https://www.home-assistant.io/integrations/mqtt/#mqtt-discovery) —
|
|
no HA YAML required. Each enclosure becomes a device with:
|
|
|
|
- a **Hotspot** sensor — the hottest reading across all SES temperature
|
|
sensors *and* the drives housed in that enclosure (drive temps are usually
|
|
the real hotspot); `drive_max_c` and `drive_temp_count` are exposed as
|
|
attributes.
|
|
- one sensor per named SES temperature element (e.g. *Temp Inlet*,
|
|
*Temp Outlet*), labelled from the SES element descriptor page.
|
|
|
|
Publishing is **opt-in**: set `MQTT_HOST` to enable it.
|
|
|
|
| Variable | Default | Description |
|
|
|---|---|---|
|
|
| `MQTT_HOST` | _(unset)_ | Broker host. Unset → MQTT disabled. |
|
|
| `MQTT_PORT` | `1883` | Broker port |
|
|
| `MQTT_USERNAME` / `MQTT_PASSWORD` | _(unset)_ | Broker credentials (fallback if OpenBao unused/unavailable) |
|
|
| `MQTT_DISCOVERY_PREFIX` | `homeassistant` | HA discovery topic prefix |
|
|
| `MQTT_BASE_TOPIC` | `jbod-monitor` | State/availability topic root |
|
|
| `MQTT_PUBLISH_INTERVAL` | `60` | Seconds between publishes |
|
|
| `MQTT_NODE_ID` | _(hostname)_ | Stable id for this monitor (disambiguates multiple hosts) |
|
|
| `MQTT_CLIENT_ID` | `jbod-monitor-<node>` | MQTT client id |
|
|
|
|
### Credentials via OpenBao
|
|
|
|
Broker credentials are sourced from OpenBao at startup rather than stored in
|
|
plaintext (matching the homelab runtime-secret pattern). Set `MQTT_SECRET_PATH`
|
|
to a KV v2 path holding `username`/`password`; the app reads
|
|
`<OPENBAO_ADDR>/v1/<OPENBAO_MOUNT>/data/<MQTT_SECRET_PATH>` with the token from
|
|
`OPENBAO_TOKEN_FILE` (or `OPENBAO_TOKEN`). If the read fails, it falls back to
|
|
the `MQTT_USERNAME`/`MQTT_PASSWORD` env vars.
|
|
|
|
| Variable | Default | Description |
|
|
|---|---|---|
|
|
| `MQTT_SECRET_PATH` | _(unset)_ | KV v2 path holding the broker creds (e.g. `home_assistant`). Unset → use env creds. |
|
|
| `MQTT_SECRET_USER_KEY` | `username` | Key within the secret for the broker username |
|
|
| `MQTT_SECRET_PASS_KEY` | `password` | Key within the secret for the broker password |
|
|
| `OPENBAO_ADDR` | `https://vault.adamksmith.xyz` | OpenBao API base URL |
|
|
| `OPENBAO_MOUNT` | `secret` | KV v2 mount point |
|
|
| `OPENBAO_TOKEN_FILE` | _(unset)_ | Path to a file containing the OpenBao token (preferred; mount read-only) |
|
|
| `OPENBAO_TOKEN` | _(unset)_ | OpenBao token (used if no token file) |
|
|
|
|
The deployed compose reads the broker user/pass from `secret/home_assistant`
|
|
(keys `mqtt_user`/`mqtt_pass`) using the read-only `claude-read` token mounted
|
|
at `/run/secrets/openbao-token`.
|
|
|
|
Topics (defaults):
|
|
|
|
```
|
|
homeassistant/sensor/<node>_enc<id>/hotspot/config # retained discovery
|
|
homeassistant/sensor/<node>_enc<id>/temp<n>/config # retained discovery
|
|
jbod-monitor/<node>/status # online | offline (LWT)
|
|
jbod-monitor/<node>/enclosure/<id>/state # {"hotspot_c":42.0, "sensor_0":28.5, ...}
|
|
```
|
|
|
|
Availability is tracked via an MQTT LWT, so entities show *unavailable* in HA
|
|
if the monitor stops.
|