# DSX plugin example

> Example ZAPP Listener deployment that uses DSX plugin components.

---

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

---

Use this example to deploy a ZAPP Listener that uses DSX plugin components to collect GPU power metrics from Redfish-capable hosts.

The standard ZAPP Listener binary includes the DSX component registrations. This example configures the registered `dsxMetrics` component type in the listener pipeline.

## Deployment pattern

Deploy the [ZAPP Aggregator](/docs/deployment/zapp-aggregator/) first. Then deploy a ZAPP Listener with a DSX pipeline.

In this pattern:

- `zapp.listeners` defines and enables the DSX Redfish listener
- `dsxMetrics` manages the host inventory and polls Redfish metrics
- `DSX_REDFISH_PROXY_ENDPOINT` configures an optional deployment-wide Redfish proxy endpoint
- `known_sensors_file` loads the chart-provided DSX sensor catalog
- `host_persistence_path` preserves host registrations in the listener data volume
- `metrics_service.listen_address` exposes the DSX management gRPC service inside the listener pod
- `aggregatorStreamer` forwards time-series telemetry to the ZAPP Aggregator

## Encryption Secret

The built-in DSX listener persists managed host registrations. Before you enable the listener, create a Kubernetes Secret for the persistence encryption key:

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: dsx-encryption
type: Opaque
data:
  key: <base64-encoded-32-byte-key>
```

The mounted Secret value must be exactly 32 bytes after Kubernetes decodes it. Use a raw AES-256 key, and keep the same key while reusing the same listener data volume.

## Metrics service TLS Secret

This example enables TLS for the DSX management gRPC service. Create a Kubernetes Secret that contains the server certificate and private key:

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: dsx-metrics-tls
type: kubernetes.io/tls
data:
  tls.crt: <base64-encoded-certificate>
  tls.key: <base64-encoded-private-key>
```

## ZAPP Listener values

Configure the DSX listener under `zapp.listeners`. Helm replaces list values as a whole. If your values file redefines `zapp.listeners`, include the complete listener entry, including `config.pipeline`, for every listener you want to keep.

```yaml
zapp:
  enabled: true
  image:
    repository: nvcr.io/nvidia/zapp
    tag: ""
    pullPolicy: IfNotPresent

  aggregatorHost: "zapp-aggregator.<namespace>.svc.cluster.local:9991"
  metricsPort: 8888
  manifestUI: true

  knownSensors:
    existingConfigMap: ""

  listeners:
    - name: redfish
      enabled: true
      replicas: 1
      grpcServices:
        zappService:
          portName: zapp-grpc
          port: 50051
        metricsManagementService:
          portName: dsx-management
          port: 9090
      encryption:
        existingSecret: dsx-encryption
        key: key
        mountPath: /secrets/dsx-encryption
      metricsServiceTLS:
        enabled: true
        transportSecurity: tls
        existingSecret: dsx-metrics-tls
        certFile: tls.crt
        keyFile: tls.key
        mountPath: /secrets/dsx-metrics-tls
      extraEnv:
        - name: DSX_REDFISH_PROXY_ENDPOINT
          value: "https://redfish-proxy.example.com"
      config:
        pipeline: |
          metricsPort: 8011
          components:
            dsxMetrics:
              type: dsxMetrics
              metrics_service:
                listen_address: :9090
                tls:
                  transportSecurity: ${env.DSX_METRICS_TRANSPORT_SECURITY}
                  certs:
                    certFile: ${env.DSX_METRICS_TLS_CERT_FILE}
                    keyFile: ${env.DSX_METRICS_TLS_KEY_FILE}
                    caFile: ${env.DSX_METRICS_TLS_CA_FILE}
              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: zappService
                  cap: 64
                  leaky: true
                - target: aggregator
                  cap: 64
                  leaky: true
            zappService:
              type: grpcServer
              address: :50051
            listener:
              type: udpListener
              network: udp
              address: :9091
              targets:
                - target: payloadProcessor
                  cap: 1000
                  leaky: true
            payloadProcessor:
              type: payloadProcessor
              timeseries:
                targets:
                  - target: aggregator
              default:
                targets:
                  - target: aggregator
            aggregator:
              type: aggregatorStreamer
```

Important fields:

- `zapp.image` selects the ZAPP Listener image that contains the DSX components
- `zapp.aggregatorHost` points the listener at the ZAPP Aggregator listener gRPC service
- `zapp.listeners[].name` becomes part of the StatefulSet and Service names
- `zapp.listeners[].enabled` enables the DSX listener entry
- `zapp.listeners[].encryption.existingSecret` points to the Secret with the raw 32-byte persistence key
- `zapp.listeners[].metricsServiceTLS` enables TLS for the DSX management gRPC service and mounts the TLS Secret
- `zapp.listeners[].extraEnv` sets the optional Redfish proxy endpoint
- `zapp.listeners[].grpcServices` exposes the DSX management and ZAPP gRPC ports through the listener Service
- `zapp.knownSensors.existingConfigMap` replaces the packaged DSX sensor catalog when you provide a ConfigMap with a `known-sensors.yaml` key
- `zapp.listeners[].config.pipeline` contains the `dsxMetrics` component and loads the mounted DSX sensor catalog with `known_sensors_file`
- `dsxMetrics.metrics_service.listen_address` opens the DSX management gRPC service inside the listener pod

## Direct Redfish mode

The listener selects direct or proxy access for each host, not for the deployment. A host registered through the DSX management API with a BMC endpoint and credentials always uses a direct Redfish session, even when `DSX_REDFISH_PROXY_ENDPOINT` is set. A host registered without credentials uses the proxy, and its registration fails to poll when no proxy endpoint is configured.

For a direct-only deployment, register every host with credentials and leave `DSX_REDFISH_PROXY_ENDPOINT` empty. The listener persists these registrations in the encrypted host inventory when `host_persistence_path` is enabled.

## Install the example

Create a values file from the DSX listener snippet, then install the listener chart.

```bash
helm upgrade --install zapp-dsx <path-to-zapp-chart> \
  --namespace <namespace> \
  --values dsx-listener-values.yaml \
  --set-string zapp.aggregatorHost=zapp-aggregator.<namespace>.svc.cluster.local:9991
```

With release name `zapp-dsx`, the built-in DSX listener StatefulSet name is `zapp-dsx-listener-redfish`.

## Validate the DSX listener

Check the listener pod:

```bash
kubectl get pods --namespace <namespace>
```

Port-forward the listener metrics endpoint:

```bash
kubectl --namespace <namespace> port-forward svc/zapp-dsx-listener-redfish 8011:8011
```

Open the manifest page:

```text
http://localhost:8011/manifest
```

If management API clients need to reach the listener through Kubernetes networking, use the headless Service ports named `dsx-management` and `zapp-grpc`.

To test the DSX management gRPC service from your workstation, port-forward the StatefulSet to the `metrics_service.listen_address` port:

```bash
kubectl --namespace <namespace> port-forward statefulset/zapp-dsx-listener-redfish 19090:9090
```

If `dsxctl` is available, query one GPU reading. Replace `node-01:0` with a registered node and GPU pair:

```bash
dsxctl --address localhost:19090 \
  --transport-security tls \
  --server-ca <path-to-server-ca> \
  query node-01:0
```
