OpenShift (OLM)#

This guide covers deploying the AMD Network Operator on OpenShift clusters using OLM (Operator Lifecycle Manager). The operator manages AMD network drivers (ionic, ionic_rdma, pds_core, tawk_ipc) using Kernel Module Management (KMM).

Prerequisites#

Infrastructure Requirements#

  • OpenShift 4.16+ cluster with CoreOS nodes

  • AMD Pensando network hardware

  • Container registry accessible from the cluster

  • Administrative access to OpenShift cluster

Required OpenShift Operators#

The following OpenShift operators must be enabled before installing the AMD Network Operator:

Service CA Operator — required for certificate signing and KMM webhook authentication:

oc get pods -A | grep service-ca

Operator Lifecycle Manager (OLM) — required for operator installation and dependencies:

oc get pods -A | grep operator-lifecycle

MachineConfig Operator — required for configuring the ionic driver blacklist:

oc get pods -A | grep machine-config

Cluster Image Registry Operator — required for driver image building and storage:

oc get pods -A | grep image-registry

Required Operators Installation#

These operators must be installed BEFORE deploying the AMD Network Operator:

1. Install Node Feature Discovery (NFD)#

NFD detects hardware features on nodes and labels them accordingly.

Installation via OpenShift Web Console:

  1. Log in to OpenShift Web Console

  2. Navigate to OperatorsOperatorHub

  3. Search for “Node Feature Discovery”

  4. Click on the operator from Red Hat

  5. Click Install

  6. Keep default settings:

    • Update Channel: Select the latest stable channel

    • Installation Mode: All namespaces on the cluster

    • Installed Namespace: openshift-nfd (auto-created)

    • Update Approval: Automatic

  7. Click Install and wait for the operator to become ready

Verification:

kubectl get csv -n openshift-nfd | grep nfd
# Expected: nfd.x.x.x    Node Feature Discovery    x.x.x    Succeeded

Create a NodeFeatureDiscovery instance to activate NFD:

After installing the NFD operator, create a NodeFeatureDiscovery CR to start NFD workers on the cluster:

  1. Navigate to OperatorsInstalled OperatorsNode Feature Discovery

  2. Click the NodeFeatureDiscovery tab

  3. Click Create NodeFeatureDiscovery

  4. Accept defaults and click Create

Or via CLI:

cat <<EOF | kubectl apply -f -
apiVersion: nfd.openshift.io/v1
kind: NodeFeatureDiscovery
metadata:
  name: nfd-instance
  namespace: openshift-nfd
spec:
  operand:
    image: quay.io/openshift/origin-node-feature-discovery:4.16
    servicePort: 12000
  workerConfig:
    configData: |
      core:
        sleepInterval: 60s
      sources:
        pci:
          deviceClassWhitelist:
            - "0200"
            - "03"
            - "12"
          deviceLabelFields:
            - "vendor"
            - "device"
EOF
# Verify NFD workers are running
kubectl get pods -n openshift-nfd | grep worker
# Expected: nfd-worker pods Running on each node

Note

Without a NodeFeatureDiscovery instance, the NFD operator is installed but idle — no node feature detection or labeling occurs.

2. Install Kernel Module Management (KMM)#

KMM manages out-of-tree kernel modules on OpenShift clusters.

Installation via OpenShift Web Console:

  1. Log in to OpenShift Web Console

  2. Navigate to OperatorsOperatorHub

  3. Search for “Kernel Module Management”

  4. Click on the operator from Red Hat

  5. Click Install

  6. Configure installation settings:

    • Update Channel: Select stable or latest channel

    • Installation Mode: All namespaces on the cluster

    • Installed Namespace: openshift-kmm (auto-created)

    • Update Approval: Automatic

  7. Click Install and wait for the operator to become ready

Verification:

kubectl get csv -n openshift-kmm | grep kernel-module-management
# Expected: kernel-module-management.v2.5.1    Kernel Module Management    2.5.1    Succeeded

kubectl get deployment -n openshift-kmm
# Expected:
# NAME                      READY   UP-TO-DATE   AVAILABLE
# kmm-operator-controller   1/1     1            1
# kmm-operator-webhook      1/1     1            1

IMPORTANT: Only install KMM once in the openshift-kmm namespace. Multiple KMM instances cause conflicts and module loading failures.

Installation#

1. Configure Insecure Registry (if using internal registry)#

OpenShift needs to trust your internal registry for pulling images without TLS:

# Check current configuration
kubectl get image.config.openshift.io/cluster -o yaml

# If your registry is not listed, add it:
kubectl patch image.config.openshift.io/cluster --type=merge \
  -p "{\"spec\":{\"registrySources\":{\"insecureRegistries\":[\"${REGISTRY_URL}\"]}}}"

Note: This configuration allows all nodes to pull from the specified registry without TLS verification.

3. Verify KMM Installation#

# Verify KMM is running in openshift-kmm namespace
kubectl get csv -n openshift-kmm | grep kernel-module-management

# Check KMM deployments
kubectl get deployment -n openshift-kmm
# Expected output:
# NAME                      READY   UP-TO-DATE   AVAILABLE
# kmm-operator-controller   1/1     1            1
# kmm-operator-webhook      1/1     1            1

Troubleshooting: If KMM exists in multiple namespaces, keep only the one in openshift-kmm to avoid conflicts.

4. Set Environment Variables#

Set version variables that will be used throughout the deployment:

# Driver and firmware versions
export DRIVERS_VERSION="1.117.5-a-56"
export KERNEL_VERSION="5.14.0-570.76.1.el9_6.x86_64"
export RHEL_VERSION="9.6"


# Registry configuration
export REGISTRY_URL="registry.test.pensando.io:5000"
export REPO_URL="https://repo.radeon.com"

# DTK image (get from: kubectl get is -n openshift driver-toolkit -o yaml)
export DTK_IMAGE="quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:288b3574a5524121c139b846e98a223da793305560f8b42dcd8d2aa712912998"

Finding Version Values:

# Get node kernel version
export KERNEL_VERSION=$(kubectl get nodes -o jsonpath='{.items[0].status.nodeInfo.kernelVersion}')

# Find matching DTK image
export DTK_IMAGE=$(kubectl get is -n openshift driver-toolkit -o jsonpath="{.spec.tags[?(@.name=='${KERNEL_VERSION}')].from.name}")

Install from OperatorHub#

This method installs the AMD Network Operator from Red Hat OperatorHub. Use this for production deployments when the operator is officially published.

Step 1: Install from OperatorHub via Web Console#

  1. Log in to OpenShift Web Console

  2. Navigate to OperatorsOperatorHub

  3. Search for “AMD Network Operator”

  4. Click on the AMD Network Operator tile

  5. Click Install

  6. Configure installation settings:

    • Update Channel: Select stable or latest channel

    • Installation Mode: Select namespace (e.g., openshift-amd-network)

    • Installed Namespace: openshift-amd-network (create if doesn’t exist)

    • Update Approval: Automatic (recommended) or Manual

  7. Click Install and wait for the operator to become ready

Step 2: Verify Installation#

# Check operator is installed
kubectl get csv -n openshift-amd-network | grep amd-network-operator
# Expected: amd-network-operator.vX.Y.Z    AMD Network Operator    X.Y.Z    Succeeded

# Verify operator pod is running
kubectl get pods -n openshift-amd-network -l control-plane=controller-manager
# Expected: STATUS Running

# Check operator logs
kubectl logs -f deployment/amd-network-operator-controller-manager -n openshift-amd-network

Step 3: Proceed to Deployment#

Once the operator is installed, proceed to the Create NetworkConfig section below to start using the operator.

Tip

If you need to pre-build driver images (for air-gapped environments or external registries), see Preparing Pre-Compiled Driver Images.


The operator creates multiple service accounts for different components:

kubectl get sa -n openshift-amd-network

# Expected service accounts:
# - amd-network-operator-controller-manager
# - amd-network-operator-device-plugin
# - amd-network-operator-kmm-module-loader
# - amd-network-operator-node-labeller
# - amd-network-operator-metrics-exporter
# - amd-network-operator-config-manager
# - amd-network-operator-utils-container

Configuration#

1. Create NFD Rule for NIC Detection#

Create a NodeFeatureRule to instruct NFD to automatically label nodes that have AMD Pensando NICs:

cat <<EOF | kubectl apply -f -
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
  name: amd-nic-label-nfd-rule
spec:
  rules:
  - name: amd-vnic
    labels:
      feature.node.kubernetes.io/amd-vnic: "true"
    matchAny:
      - matchFeatures:
          - feature: pci.device
            matchExpressions:
              vendor: {op: In, value: ["1dd8"]}
              device: {op: In, value: ["1003"]}
              subsystem_vendor: {op: In, value: ["1dd8"]}
              subsystem_device: {op: In, value: ["5201"]}
  - name: amd-nic
    labels:
      feature.node.kubernetes.io/amd-nic: "true"
    matchAny:
      - matchFeatures:
          - feature: pci.device
            matchExpressions:
              vendor: {op: In, value: ["1dd8"]}
              device: {op: In, value: ["1002"]}
              subsystem_vendor: {op: In, value: ["1dd8"]}
              subsystem_device: {op: In, value: ["5201"]}
EOF

Verify that nodes with AMD Pensando NICs are labeled:

kubectl get nodes -l feature.node.kubernetes.io/amd-nic=true

Note

This requires the NFD operator to be installed and a NodeFeatureDiscovery CR to be created (see Prerequisites). NFD will automatically apply the feature.node.kubernetes.io/amd-nic: "true" label to any node with AMD Pensando PCI devices (vendor 1dd8). No node reboot is required.

2. Create NetworkConfig#

Create the NetworkConfig CR to deploy drivers on your nodes. Choose the configuration based on which build method you used:

Option 1: RPM-based Build (if you used Method 1):

cat <<EOF | kubectl apply -f -
apiVersion: amd.com/v1alpha1
kind: NetworkConfig
metadata:
  name: amd-network
  namespace: openshift-amd-network
spec:
  selector:
    feature.node.kubernetes.io/amd-nic: "true"

  driver:
    enable: true
    version: "${DRIVERS_VERSION}"
    useSourceImage: false  # Use RPM-based build
    image: ${REGISTRY_URL}/amdnetwork_kmod
    imageRegistrySecret:
      name: global-pull-secret  # Optional: if registry requires auth
    imageRegistryTLS:
      insecure: true
      insecureSkipTLSVerify: true
    imageBuild:
      baseImageRegistryTLS:
        insecure: true
        insecureSkipTLSVerify: true
    AMDNetworkInstallerRepoURL: "${REPO_URL}"

  devicePlugin:
    enableNodeLabeller: true
    devicePluginImage: docker.io/rocm/k8s-network-device-plugin:latest
    nodeLabellerImage: docker.io/rocm/k8s-network-node-labeller:latest

  metricsExporter:
    enable: true
    image: docker.io/rocm/device-metrics-exporter:nic-v1.2.0
EOF

Option 2: Source Image Build (for air-gapped environments or when using pre-built source images):

cat <<EOF | kubectl apply -f -
apiVersion: amd.com/v1alpha1
kind: NetworkConfig
metadata:
  name: amd-network
  namespace: openshift-amd-network
spec:
  selector:
    feature.node.kubernetes.io/amd-nic: "true"

  driver:
    enable: true
    version: "${DRIVERS_VERSION}"
    useSourceImage: true  # Use source image build
    image: ${REGISTRY_URL}/amdnetwork_kmod
    imageBuild:
      sourceImageRepo: "docker.io/amdpsdo/amdnic-drivers"  # Pre-built source images
      baseImageRegistryTLS:
        insecure: true
        insecureSkipTLSVerify: true
    imageRegistrySecret:
      name: global-pull-secret  # Optional: if registry requires auth
    imageRegistryTLS:
      insecure: true
      insecureSkipTLSVerify: true
    AMDNetworkInstallerRepoURL: "${REPO_URL}"

  devicePlugin:
    enableNodeLabeller: true
    devicePluginImage: docker.io/rocm/k8s-network-device-plugin:latest
    nodeLabellerImage: docker.io/rocm/k8s-network-node-labeller:latest

  metricsExporter:
    enable: true
    image: docker.io/rocm/device-metrics-exporter:nic-v1.2.0
EOF

3. Monitor Deployment#

# Watch NetworkConfig status
kubectl get networkconfig -n openshift-amd-network -w

# Check KMM Module creation
kubectl get module -n openshift-amd-network

# View Module status
kubectl get module amd-network -n openshift-amd-network -o yaml

# Check driver pods
kubectl get pods -n openshift-amd-network

Preparing Pre-Compiled Driver Images (Optional)#

Warning

For most users with connected clusters, you can skip this entire section and proceed directly to the Create NetworkConfig section below. When you create a NetworkConfig CR, KMM will automatically build driver images in-cluster using the OpenShift internal registry.

When to use this section:

  • Air-gapped/disconnected environments: No internet access during runtime

  • Pre-staging images: Want driver images ready before deployment

  • External registry requirements: Need images in a specific external registry

  • Custom build pipelines: Integrating with CI/CD systems

When to skip this section:

  • Connected clusters: Have internet access to repo.radeon.com

  • Quick start/trial: Want the fastest path to running drivers

  • Using internal registry: OpenShift’s built-in registry is sufficient

Tip

If you do choose to pre-build images, you can do this in parallel while the operator deploys. The operator will wait idle until you create a NetworkConfig CR.


Two Driver Image Build Methods#

The operator supports two methods for building driver images, controlled by the useSourceImage field in NetworkConfig CR:

Method 1: RPM-based Build (useSourceImage: false) - Recommended:

  • Downloads pre-compiled RPM packages from repo.radeon.com

  • Installs drivers directly from RPMs

  • Faster build process

  • Uses: DockerfileTemplate.rpm.ionic.coreos

Method 2: Source Image Build (useSourceImage: true) - Advanced:

  • Requires building a source image first containing driver source code

  • KMM compiles modules from source against specific kernel

  • More flexible for custom builds

  • Uses: DockerfileTemplate.srcimg.ionic.coreos + source image from internal-example/driverSrcImage/Dockerfile.ionic.coreos



Method 2: Source Image Build (Advanced)#

This method first builds a source container image, then KMM compiles modules from that source against the specific kernel.

Note

Air-gapped environments: This approach is designed for air-gapped or disconnected environments where direct access to external repositories (like repo.radeon.com) is restricted. By building a source image first, all required driver sources are packaged into a container that can be transferred and used in isolated environments without internet access during module compilation.

Step 5a (Alternative): Build Source Image Manually#

If you need to build source images yourself (e.g., for a custom driver version or internal registry):

Option 1: Using the automated builder script:

cd internal-example/driverSrcImage
./build-all-source-images.sh --version ${DRIVERS_VERSION} --registry your-registry.com

Option 2: Using OpenShift BuildConfig:

cat > /tmp/source-image-build.yaml << EOF
apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
  name: amdainic-driver-source
  namespace: openshift-amd-network
spec:
  lookupPolicy:
    local: true
---
apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
  name: amd-source-image-build
  namespace: openshift-amd-network
spec:
  output:
    to:
      kind: ImageStreamTag
      name: amdainic-driver-source:latest
  source:
    type: Git
    git:
      uri: "https://github.com/ROCm/network-operator.git"
      ref: "main"
    contextDir: "internal-example/driverSrcImage"
    dockerfile: "Dockerfile.ionic.coreos"
  strategy:
    type: Docker
    dockerStrategy:
      buildArgs:
        - name: REPO_URL
          value: "${REPO_URL}"
        - name: MAJOR_VERSION
          value: "9"
        - name: DRIVERS_VERSION
          value: "${DRIVERS_VERSION}"
      forcePull: true
  triggers: []
EOF

kubectl apply -f /tmp/source-image-build.yaml
kubectl start-build amd-source-image-build -n openshift-amd-network
kubectl logs -f build/amd-source-image-build-1 -n openshift-amd-network

What source images contain:

  • /ionic_src/driver/ - Source code for ionic, pds, tawk-ipc modules

  • /ionic_src/firmware/ - Firmware files

Step 5b: Push Source Image to External Registry (If Built Manually)#

Skip this step if using pre-built images from docker.io/amdpsdo/amdnic-drivers.

# SSH to a node
NODE_IP=<your-node-ip>
ssh core@${NODE_IP}

# Find and push the source image
sudo podman images | grep amdainic-driver-source

sudo podman tag <image-id> \
  ${REGISTRY_URL}/amdainic-driver-source:${DRIVERS_VERSION}

sudo podman push --tls-verify=false \
  ${REGISTRY_URL}/amdainic-driver-source:latest

Step 5c: Configure NetworkConfig to Use Source Image#

When creating your NetworkConfig CR, set useSourceImage: true and provide the source image repository:

spec:
  driver:
    enable: true
    useSourceImage: true  # Enable source image build
    version: "${DRIVERS_VERSION}"
    image: ${REGISTRY_URL}/amdnetwork_kmod  # Final driver image (compiled .ko files)
    imageBuild:
      sourceImageRepo: "docker.io/amdpsdo/amdnic-drivers"  # Pre-built source images

Tip

Note: If using a custom/internal source image registry, replace docker.io/amdpsdo/amdnic-drivers with your registry path.

How it works:

  1. KMM uses DockerfileTemplate.srcimg.ionic.coreos

  2. Copies source code from your source image (sourceImageRepo)

  3. Compiles modules against the Driver Toolkit (DTK) for the specific kernel version

  4. Creates final driver image with compiled .ko files


Validation#

Complete Validation Checklist#

# 1. Operator Running
kubectl get pods -n openshift-amd-network -l control-plane=controller-manager
# Status: Running

# 2. NetworkConfig Applied
kubectl get networkconfig -n openshift-amd-network
# Status: Should show your config

# 3. KMM Module Created
kubectl get module -n openshift-amd-network
# Status: moduleLoader.nodesMatchingSelectorNumber should match node count

# 4. Device Plugin Running
kubectl get pods -n openshift-amd-network -l app=device-plugin
# Status: Running on target nodes

# 5. Node Labeller Running
kubectl get pods -n openshift-amd-network -l app=node-labeller
# Status: Running on target nodes

# 6. Drivers Loaded on Node
oc debug node/<node-name> -- chroot /host lsmod | grep -E '^(ionic|pds_core|tawk_ipc)'
# Expected: ionic, ionic_rdma, pds_core, tawk_ipc modules loaded

# 7. RDMA Devices Available
oc debug node/<node-name> -- chroot /host ls /sys/class/infiniband/
# Expected: ionic_0, ionic_1, ... (one per NIC)

Workload Configuration#

iommu=pt Kernel Parameter#

GPU and RDMA workloads require IOMMU passthrough mode. Create a MachineConfig:

apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
  labels:
    machineconfiguration.openshift.io/role: master
  name: 99-iommu-passthrough
spec:
  kernelArguments:
    - iommu=pt

Warning

This triggers a rolling reboot of all nodes in the MachineConfigPool.

Verify after reboot:

cat /proc/cmdline | grep iommu
dmesg | grep "Default domain type"

Host NIC IP Configuration#

On OpenShift (RHCOS), use nmstate for configuring static IPs on host NIC interfaces. For testing, apply directly: sudo nmstatectl apply <file>.yaml. For production, use the NMState Operator with NodeNetworkConfigurationPolicy (NNCP) CRDs.

For backend network configuration options, see the RoCE Cluster Network Configuration Guide.

Uninstallation#

Remove Operator Completely#

# Delete all NetworkConfig CRs first
oc delete networkconfigs.amd.com -n openshift-amd-network --all

# Delete the operator subscription and CSV
oc delete subscription amd-network-operator -n openshift-amd-network
oc delete csv -n openshift-amd-network -l operators.coreos.com/amd-network-operator.openshift-amd-network

# Delete CatalogSource (if using catalog method)
oc delete catalogsource amd-network-operator-catalog -n openshift-marketplace

# Delete namespace (optional)
oc delete namespace openshift-amd-network