# DSX plugin

> Configuration overview for the DSX plugin components used by ZAPP Listener pipelines.

---

LLMS index: [llms.txt](/datacenter/zapp/llms.txt)

---

The DSX plugin adds pipeline components for collecting GPU and platform power metrics from Redfish-capable DSX hosts. Use it when a ZAPP Listener needs to manage a DSX host inventory, poll Redfish metric reports, and optionally emit selected Redfish metrics as ZAPP time-series telemetry.

The current ZAPP Listener binary registers the DSX component types. If you build a custom listener image, confirm that the image includes the DSX component registrations before you use these component types in a pipeline.

For a complete Helm values example, see [DSX plugin example](/docs/examples/dsx-plugin/).

## Components

The DSX plugin provides the `dsxMetrics` pipeline component:

| Component | Purpose |
| --- | --- |
| `dsxMetrics` | Polls DSX hosts through direct Redfish sessions or a deployment-wide Redfish proxy, exposes the DSX management gRPC service, and can emit time-series telemetry to downstream targets. |

## Required configuration

A useful `dsxMetrics` deployment needs these settings:

- `type: dsxMetrics`
- `metrics_service.listen_address` for the DSX management gRPC service
- A host-registration workflow that uses the DSX management API
- `sensor_polls` or `known_sensors_file` for the Redfish sensor catalog
- `targets` when you want DSX Redfish metrics forwarded as ZAPP time-series telemetry
- `host_persistence_path` and listener-wide encryption when you want host registrations preserved across restarts

The Helm chart's built-in DSX listener uses `known_sensors_file` and mounts the packaged sensor catalog at `/etc/zapp/known-sensors/known-sensors.yaml`:

```yaml
components:
  dsxMetrics:
    type: dsxMetrics
    metrics_service:
      listen_address: ":9090"
    host_persistence_path: /data
    max_hosts: 10000
    proxy:
      endpoint: ${env.DSX_REDFISH_PROXY_ENDPOINT}
    known_sensors_file: /etc/zapp/known-sensors/known-sensors.yaml
    targets:
      - target: aggregator
        cap: 64
        leaky: true

  aggregator:
    type: aggregatorStreamer
```

## Redfish access modes

`dsxMetrics` selects the access mode for each registered host, not for the component as a whole.

- **Direct mode.** A host registered with a BMC `endpoint` and credentials always uses a direct Redfish session, even when `proxy.endpoint` is configured.
- **Proxy mode.** A host registered without credentials is polled through the shared Redfish proxy. Registering such a host fails when no proxy endpoint is configured.

Configure the proxy with `proxy.endpoint`, set to an `http` or `https` URL with a host. Omit the `proxy` block or leave the endpoint empty for a direct-only deployment.

Hosts are registered through the DSX management API. See [Management API](#management-api).

## Known sensors

The DSX listener uses known-sensor definitions to map Redfish resources and properties to stable ZAPP sensor IDs, units, and metric metadata. The Helm chart mounts the packaged `known-sensors.yaml` catalog by default.

Use this reference when you review the packaged catalog or provide a replacement catalog with `zapp.knownSensors.existingConfigMap` or inline `sensor_polls`. Do not configure both replacement methods in the same `dsxMetrics` component.

Templates apply to entries under `sensor_polls[].known_sensors[]`.

### Template locations

Only these fields accept templates:

- `sources[].resource`
- `sources[].property`

The `metric.resourceType` field describes the metric type. It does not select expansion or discovery behavior.

### Expansion modes

| Field | Behavior |
| --- | --- |
| `count: N` | Creates base indexes `0` through `N-1`. |
| `expandBy: GPU` | Creates one sensor for each discovered GPU `Processor`. |
| `expandBy: CPU` | Creates one sensor for each discovered CPU `Processor`. |
| No `count`, `expandBy`, or templates | Creates one singleton sensor. |

`count` and `expandBy` are mutually exclusive. A known-sensor entry that uses a template must set `count` or `expandBy`.

### Index context

Use these rules when a template includes `{{.index}}`:

- `{{.index}}` is the base index plus the source's `indexOffset`
- `indexOffset` defaults to `0` and can differ between source candidates
- The sensor ID and `metric.index` use the base index without offset
- `GPUIndex` also uses the base index for GPU-expanded sensors

### Discovery context

Discovery context fields are case-sensitive:

| Field | Value |
| --- | --- |
| `{{.device.id}}` | Exact Redfish `Id` |
| `{{.device.uuid}}` | Canonical UUID without a type prefix |
| `{{.device.serialNumber}}` | Optional Redfish `SerialNumber` |
| `{{.device.model}}` | Optional Redfish `Model` |
| `{{.device.uri}}` | Exact Redfish `@odata.id` |

GPU expansion can also provide linked memory and chassis resources. Each linked resource has `id`, `uuid`, `serialNumber`, `model`, and `uri` fields. For example, use `{{.device.memory.uri}}` or `{{.device.chassis.id}}`.

CPU discovery uses the same root `device` fields with `expandBy: CPU`. CPU templates cannot use `{{.device.memory.*}}` or `{{.device.chassis.*}}`.

If a source references a missing optional value, the listener skips that source and tries the next source. If no source remains, the listener omits the sensor instance.

### Template syntax

Use plain text and direct field actions from this reference. Functions, pipelines, variables, `if`, `with`, `range`, and nested templates are invalid.

### Examples

Fixed numeric expansion:

```yaml
count: 8
sources:
  - resource: /redfish/v1/Chassis/GPU_{{.index}}
    property: $.Readings[{{.index}}].Value
  - resource: /redfish/v1/Chassis/GPU_SXM_{{.index}}
    indexOffset: 1
    property: $.Reading
```

GPU discovery expansion:

```yaml
- baseSensorId: 280
  expandBy: GPU
  unit: PowerW
  metric:
    schema: dps.metrics/v1
    resourceType: GPU
    name: powerLimitSetPointWatts
  sources:
    - resource: '{{.device.uri}}/EnvironmentMetrics'
      property: $.PowerLimitWatts.SetPoint
    - resource: '{{.device.memory.uri}}/EnvironmentMetrics'
      property: $.PowerWatts.Reading
```

CPU discovery expansion:

```yaml
- baseSensorId: 300
  expandBy: CPU
  unit: PowerW
  metric:
    schema: dps.metrics/v1
    resourceType: CPU
    name: powerWatts
  sources:
    - resource: '{{.device.uri}}/EnvironmentMetrics'
      property: $.PowerWatts.Reading
```

UUID-based source matching:

```yaml
sources:
  - resource: /redfish/v1/TelemetryService/MetricReports/NvidiaNMMetrics_0
    property: '$.MetricValues[?(@.MetricId == "{{.device.uuid}}")].MetricValue'
```

## Management API

The `metrics_service.listen_address` field exposes the DSX `MetricsManagementService` gRPC API inside the listener pod. The API supports:

- `AddHosts` to register hosts at runtime
- `RemoveHosts` to remove hosts by name
- `GPUMetricsQuery` to query GPU power readings by node and GPU ID

Configure `metrics_service.tls` when clients require transport security. If you configure the `tls` block, set `tls.transportSecurity`.

## Persistence

Set `host_persistence_path` only when the listener should preserve managed host inventory across restarts. The path must already exist as a directory, and the listener must have top-level `encryption.key_file` configuration that points to a mounted raw AES-256 key file.

For a Helm chart DSX listener entry, set `zapp.listeners[].encryption.existingSecret` to an existing Kubernetes Secret. By default, the chart mounts the Secret key named `key` at `/secrets/dsx-encryption/key` and configures the listener to use that file. The mounted file must contain exactly 32 bytes after Kubernetes decodes the Secret data. Do not use a passphrase, text encoding, or a value with a trailing newline.

Keep the same encryption key while reusing the same persisted host data. Replacing the key prevents the listener from decrypting the existing host registrations.

The listener stores every host registration in one encrypted file in the persistence directory. A restored registration keeps the access mode it was created with, so a host that was registered for proxy access fails to poll if the listener that restores it has no `proxy.endpoint`. Do not reuse a persistence directory across listeners with different Redfish access configurations.
