> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sambanova.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# PDU exporter

The PDU exporter publishes power metrics for Vertiv/Geist rack PDUs. One exporter instance serves any number of PDUs. Prometheus scrapes `/probe?target=<address>` once per PDU.

| Property | Value |
| :- | :- |
| Port | 9820 by default. Set `pdu-exporter.port`. |
| Runs as | A Deployment, deployed by the `sambanova-hardware-monitoring` chart |
| Device protocol | The PDU's HTTP API |
| Authentication | None. The exporter reads the PDU as a guest. |
| Network | The cluster nodes need a route to the PDUs on the management network |
| Image version | 0.1.2 or later. Earlier builds cannot read PDU firmware 5.10.8. |

## Endpoints

| Endpoint | Returns |
| :- | :- |
| `/probe?target=<address>` | The metrics for one PDU |
| `/metrics` | The exporter's own process metrics and probe and cache counters. Contains no PDU data. |
| `/healthz` | `ok` while the exporter is running |

`/probe` accepts one optional parameter: `outlets=true` adds per-outlet measurements. It is off by default because it adds one series per outlet.

## Metrics

| Metric | Type | Labels | Description |
| :- | :- | :- | :- |
| `pdu_power_watts` | gauge | `device`, `level`, `entity`, `label` | Real power in watts |
| `pdu_measurement_unavailable` | gauge | `device`, `level`, `entity`, `type` | `1` when the PDU reports a reading as stale or unparseable. The reading itself is then absent. |
| `pdu_info` | gauge | `device`, `label`, `state`, `model`, `firmware`, `serial`, `part` | PDU identity. Always `1`. |
| `pdu_up` | gauge | none | `1` when the PDU answered, `0` when it did not |
| `pdu_scrape_duration_seconds` | gauge | none | Time taken to read the PDU |

<Note>
  This is the complete supported set. A PDU measures a great deal more and the exporter reads it, but only these series are published. If you see additional series on an older exporter image, do not build dashboards or alerts on them. They are removed in current images.
</Note>

`pdu_measurement_unavailable` has no samples while every reading is healthy, so an install verified against working hardware does not show it. It appears only when the PDU reports a measurement it has lost.

## Labels

| Label | Values | Description |
| :- | :- | :- |
| `pdu` | The target's `name`, or its address | Identifies the PDU. Added by Prometheus, not the exporter. |
| `rack` | Whatever you declare | Declared in the target's `labels`. The exporter cannot tell which rack a PDU is in, so per-rack panels require it. |
| `device` | A 16-character identifier | The PDU's own device ID |
| `level` | `total`, `phase`, `line`, `breaker`, `outlet` | The aggregation point of the reading. On `pdu_power_watts` and `pdu_measurement_unavailable`. |
| `entity` | `phase0`, `breaker3`, `total0`, an outlet index | The PDU's own name for that point. Stable across scrapes. |
| `label` | `Phase A`, or empty | The name an operator set on the PDU. On `pdu_power_watts` and `pdu_info`. |
| `type` | `voltage`, `current`, `realPower`, `apparentPower`, `powerFactor`, `energy` | The measurement that went unavailable, including ones not published. On `pdu_measurement_unavailable`. |
| `state` | `normal`, and other PDU-reported states | On `pdu_info` |
| `model`, `firmware`, `serial`, `part` | Hardware identity | On `pdu_info` |

<Warning>
  Always filter power on `level="total"`. A PDU meters the same power at several levels, so an unfiltered `sum(pdu_power_watts)` counts the same watts more than once and roughly triples the real figure while still looking plausible.
</Warning>

```promql theme={}
# Whole-device draw, per PDU
pdu_power_watts{level="total"}

# Draw per rack
sum by (rack) (pdu_power_watts{level="total"})
```

## Behavior

| Behavior | Detail |
| :- | :- |
| An unreachable PDU returns HTTP 200 with `pdu_up 0` | A non-200 response would make Prometheus discard the whole scrape, including the `up` sample. Alert on `pdu_up == 0`. |
| A lost reading is absent, not zero | The exporter sets `pdu_measurement_unavailable` to `1` instead. Alert on that, not on a missing value. |
| Scrape no faster than every 30 seconds | A PDU takes 2.2 to 2.8 seconds to answer. The exporter enforces a minimum scrape timeout of 5 seconds and refuses to start below it. It caches each PDU's read for 15 seconds. |
| Extra replicas multiply PDU load | Replicas do not share a cache. Keep `replicaCount` at `1`. |

## Configuration

### Helm values

| Key | Default | Description |
| :- | :- | :- |
| `pdu-exporter.enabled` | `true` | Deploy the exporter |
| `pdu-exporter.probe.enabled` | `true` | Create the `Probe` that scrapes the PDUs. Without it, nothing scrapes them. |
| `pdu-exporter.probe.interval` | `30s` | Scrape interval. Do not shorten it. |
| `pdu-exporter.probe.scrapeTimeout` | `15s` | Prometheus scrape timeout. Must be longer than `pdu-exporter.scrapeTimeout`. |
| `pdu-exporter.probe.labels` | `{}` | Must match your Prometheus `probeSelector` |
| `pdu-exporter.targets` | `[]` | Required when enabled. See [Targets](#targets). |
| `pdu-exporter.outlets` | `false` | Report outlet measurements for all PDUs. A per-target `outlets` value wins. |
| `pdu-exporter.cacheTTL` | `15s` | How long one PDU's read is reused |
| `pdu-exporter.scrapeTimeout` | `10s` | Exporter-side timeout. Minimum `5s`. |
| `pdu-exporter.withSystem` | `true` | Read the identity block for `pdu_info` |
| `pdu-exporter.logLevel` | `info` | `debug`, `info`, `warn`, or `error` |
| `pdu-exporter.serviceMonitor.enabled` | `false` | Scrape the exporter's own `/metrics` |
| `pdu-exporter.serviceMonitor.interval` | `60s` | Applies to the exporter's own endpoint only. `probe.interval` governs how often PDUs are read. |
| `pdu-exporter.serviceMonitor.scrapeTimeout` | `10s` | Must not exceed the interval |
| `pdu-exporter.replicaCount` | `1` | Number of exporter pods |
| `pdu-exporter.port` | `9820` | Port the exporter listens on |
| `pdu-exporter.resources` | 50m / 64Mi, limit 256Mi | Pod requests and limits |
| `pdu-exporter.nodeSelector` / `.tolerations` / `.affinity` | `{}` / `[]` / `{}` | Pin the pod to nodes that can reach the management network |
| `pdu-exporter.podSecurityContext` / `.securityContext` | non-root, read-only rootfs, drop ALL | Hardened by default |

### Targets

Each target needs only `address`. A bare string is equivalent to `{address: <string>}`.

```yaml theme={}
pdu-exporter:
  enabled: true
  probe:
    enabled: true
  targets:
    - address: <pdu-1-ip>
      name: <rack-id>-pdu1
      labels:
        rack: "<rack-id>"
    - <pdu-2-ip>                    # no rack label: excluded from per-rack panels
    - address: <pdu-3-ip>
      outlets: true
```

| Field | Description |
| :- | :- |
| `address` | Required. Goes into `?target=` as written. Use a hostname or IP address, optionally with `:port`, and no scheme, path, or space. |
| `name` | Replaces the address as the `pdu` label |
| `labels` | Each key becomes a label on every series from this PDU |
| `outlets` | Report outlet measurements for this PDU only |

## Next steps

* [Install monitoring](monitoring-install)
* [Reference dashboards](monitoring-reference-dashboards)
* [Troubleshooting](monitoring-troubleshooting)
