Skip to content

OpenStack Plugin RHOSO 18 Installation#

This guide provides step-by-step instructions for installing the Nokia EDA Connect OpenStack plugin on Red Hat OpenStack Services on OpenShift (RHOSO) 18.0 using the OpenStack Operator on Red Hat OpenShift Container Platform (RHOCP).

Warning

Before proceeding with this installation method, ensure you have completed all the prerequisites and preparation steps described in the OpenStack Plugin Installation guide.

Prerequisites#

  • All prerequisites from the OpenStack Plugin Installation guide must be met
  • EDA Kubernetes preparation steps (Service Account and Token) must be completed
  • A RHOCP cluster that meets RHOSO 18.0 requirements. Red Hat documents validated combinations for each RHOSO release; plan the cluster using Planning your deployment and Preparing RHOCP for RHOSO
  • OpenStack Operator installed in the openstack-operators namespace (see Installing and preparing the OpenStack Operator)
  • Access to Nokia EDA Connect container images on registry.connect.redhat.com/nokia-ni for the Neutron API and openstackclient images used on RHOSO 18
  • Administrative access to the OpenShift cluster (oc) and to the EDA Kubernetes cluster (kubectl)
Getting the container registry credentials

Contact your Red Hat representative to obtain credentials for accessing the Nokia container images in the Red Hat Container Catalog. Configure registry.connect.redhat.com in the cluster pull secret (or equivalent image pull authentication for the openstack namespace) so worker nodes can pull the Neutron and openstackclient images after you reference them in OpenStackVersion.

RHOSP 17.1 vs. RHOSO 18
Aspect RHOSP 17.1 (TripleO) RHOSO 18 (OCP)
Neutron deployment Podman containers via Heat Kubernetes pods via OpenStack Operator
EDA ML2 settings TripleO environment file Kubernetes Secret mounted into Neutron pods
Nokia container images container-image-prepare overrides OpenStackVersion.spec.customContainerImages
CA certificate inject-trust-anchor Heat environment Kubernetes Secret and volume mount
LLDP on computes Post-deploy Ansible playbook OpenStackDataPlaneService on the data plane

Installation Steps#

Step 1: Prepare EDA specific artifacts#

EDA CA Certificate Secret (If Required)#

If the EDA K8S API uses a self-signed certificate, store the CA in a Secret in the openstack namespace. The file should contain the PEM CA Certificate you retrieved earlier (see also OpenStack Plugin Installation).

Trusted public CA

If EDA uses certificates signed by a well-known certificate authority, skip creating eda-ca-secret, omit the CA volume from extraMounts, and remove the ca_cert_path setting from the ML2 fragment in Step 5.

oc create secret generic eda-ca-secret \
  --from-file=eda.crt=/path/to/eda-ca.crt \
  -n openstack

Replace /path/to/eda.crt with the path to the saved CA certificate.

Neutron ML2 EDA Connect Secret#

Create a Secret that holds the [ml2_eda_connect] configuration consumed by Neutron. Use a temporary file, then create the secret:

[DEFAULT]
nic_mapping_provisioning = True

[ml2_eda_connect]
plugin_name = <OPENSTACK_PLUGIN_NAME>
api_host = https://<EDA_API_HOST>:6443
api_namespace = <EDA_NAMESPACE>
api_token = <SERVICE_ACCOUNT_TOKEN>
ca_cert_path = /etc/pki/ca-trust/source/anchors/eda.crt
heartbeat_interval = 10
cat > /tmp/eda-ml2.conf <<'EOF'
[DEFAULT]
nic_mapping_provisioning = True

[ml2_eda_connect]
plugin_name = <OPENSTACK_PLUGIN_NAME>
api_host = https://<EDA_API_HOST>:6443
api_namespace = <EDA_NAMESPACE>
api_token = <SERVICE_ACCOUNT_TOKEN>
ca_cert_path = /etc/pki/ca-trust/source/anchors/eda.crt
heartbeat_interval = 10

EOF

oc create secret generic nokia-eda-ml2-config \
  --from-file=eda.conf=/tmp/eda-ml2.conf \
  -n openstack
rm /tmp/eda-ml2.conf

Update the placeholders in the fragment before creating the secret:

plugin_name
A unique name for this OpenStack deployment in EDA (for example rhoso-prod)

Plugin name requirements

The plugin name must comply with the regex '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]', contain only alphanumerics and ., _, -, start with an alphanumeric, and be at most 63 characters.

api_host
EDA Kubernetes API hostname (for example api.eda.example.com); the URI in the file must include https:// and port :6443 as shown
api_namespace
Namespace where the fabric is configured in EDA
api_token
Bearer token from the Create a Service Account Token section
ca_cert_path
Use /etc/pki/ca-trust/source/anchors/eda.crt when using eda-ca-secret; omit the line entirely if EDA uses a public CA
heartbeat_interval
Optional; shown as 10 seconds in the example

Step 2: OpenStack Operator and Namespaces#

On the OpenShift cluster, create the operator and workload namespaces and install the OpenStack Operator following Red Hat's guide:

Use the YAML from the current RHOSO documentation for your release rather than copying abbreviated examples here. Follow chapters 1, 2 and 3 in the guide, continue here once you get to Chapter 4: "Creating the control plane".

Step 3: OpenStack Control Plane — Neutron and EDA Mounts#

During chapter 4 of the Red Hat guide, you will have to adjust some steps as detailed below.

Build your OpenStackControlPlane custom resource from Creating the control plane. Merge Red Hat’s full control plane spec (DNS, Keystone, Nova, Cinder, Glance, storage class, secrets, and so on) with the adjusted Neutron settings below.

For EDA Connect, we will heavily modify the neutron section of the OpenStackControlPlane custom resource, as touched upon in the Red Hat Configuring network services and Integrating partner content]guide:

Configuration example

The below configuration is an example of the neutron section of the OpenStackControlPlane custom resource. Adapt it to your environment. Important elements of the configuration have been annotated with (+) to explain their purpose.

apiVersion: core.openstack.org/v1beta1
kind: OpenStackControlPlane
metadata:
  name: openstack
  namespace: openstack
spec:
  secret: osp-secret
  storageClass: <STORAGE_CLASS>       # e.g. ocs-storagecluster-ceph-rbd

  # ... (DNS, Keystone, Nova, Cinder, Glance, etc. — see upstream guide)
  # https://docs.redhat.com/en/documentation/red_hat_openstack_services_on_openshift/18.0/html/deploying_red_hat_openstack_services_on_openshift/assembly_creating-the-control-plane

  neutron:
    enabled: true
    apiOverride:
      route: {}
    template:
      databaseInstance: openstack
      secret: neutron-secret
      networkAttachments:
        - internalapi
      replicas: 3

      customServiceConfig: | #(2)!
        [DEFAULT]
        debug = True
        service_plugins = segments,trunk,qos,port_forwarding,placement,nic_mapping

        [ml2]
        type_drivers = flat,vlan,vxlan
        tenant_network_types = vlan,vxlan
        mechanism_drivers = ovn,eda_connect,sriovnicswitch
        extension_drivers = eda_network,port_security,qos,uplink_status_propagation,dns_domain_keywords

        [ml2_type_vlan]
        network_vlan_ranges = datacentre:1:4094

      # Mount the EDA ML2 config secret and CA cert into the Neutron pod
      extraMounts: 
        - extraVol:
            - mounts:
                - name: nokia-eda-config #(3)!
                  mountPath: /etc/neutron/neutron.conf.d/ml2_eda_connect.conf
                  subPath: ml2_eda_connect.conf
                  readOnly: true
                - name: eda-ca-cert #(4)!
                  mountPath: /etc/pki/ca-trust/source/anchors/eda.crt
                  subPath: eda.crt
                  readOnly: true
              volumes:
                - name: nokia-eda-config #(3)!
                  secret:
                    secretName: nokia-eda-ml2-config
                - name: eda-ca-cert #(4)!
                  secret:
                    secretName: eda-ca-secret
  1. Custom Neutron image from registry.connect.redhat.com/nokia-ni that includes the eda_connect ML2 driver, adjust the image tag to the appropriate one for your environment.
  2. We override the configuration of the Neutron service to include the eda_connect mechanism driver and the eda_network extension driver. Note that all other configuration should be adapted to your environment, you can omit any entries to use the default values. Note that layer 3 OVN functionality is not supported together with the EDA Connect OpenStack driver.
  3. We mount the nokia-eda-ml2-config from Step 1 to provide the EDA Connect OpenStack ML2 plugin with its configuration.
  4. If the EDA K8S API uses a self-signed CA, we mount that CA in the pod to validate communication with the EDA K8S API. Leave this block out if the EDA K8S API is signed by an official CA.

Do not set a custom Neutron containerImage on the control plane for the Nokia image. Image overrides are applied in Step 8 via OpenStackVersion so they persist across operator reconciliation.

Apply your control plane manifest with the configuration for all other services as normal and continue with step 4.

Step 4: Nokia Container Images Override#

To adapt the deployment with the correct images for the EDA Connect OpenStack driver, adapt the OpenStackVersion CR. The OpenStackVersion object is created when the OpenStack Operator reconciles the control plane. Wait until it exists, then patch spec.customContainerImages so Neutron API and the openstackclient deployment use Nokia images that include the EDA mechanism driver and the python-eda-openstackclient extension.

oc wait openstackversion openstack -n openstack \
  --for=condition=Initialized \
  --timeout=120s
oc patch openstackversion openstack -n openstack \
  --type merge -p '{
    "spec": {
      "customContainerImages": {
        "neutronAPIImage": "registry.connect.redhat.com/nokia-ni/rhoso18-openstack-neutron-server-nokia-eda:<TAG>",
        "openstackClientImage": "registry.connect.redhat.com/nokia-ni/rhoso18-openstack-openstackclient-nokia-eda:<TAG>"
      }
    }
  }'

Replace <TAG> with the image tags supplied by Nokia or Red Hat. Ensure the cluster can authenticate to registry.connect.redhat.com (for example via the global pull secret), as described under Getting the container registry credentials in the prerequisites.

Verify the patch of the OpenStackVersion CR:

oc get openstackversion openstack -n openstack \
  -o jsonpath='{.spec.customContainerImages}' | jq .

Wait for the control plane to become ready after the patch:

oc wait openstackcontrolplane openstack \
  --for=condition=Ready \
  --timeout=1200s \
  -n openstack

Verify the EDA Connect ML2 driver was loaded:

# Get the neutron-server pod name
NEUTRON_POD=$(oc get pods -n openstack -l service=neutron -o jsonpath='{.items[0].metadata.name}')

# Check logs for EDA Connect initialization
oc logs ${NEUTRON_POD} -n openstack | grep -i "eda_connect"
Expected output should include lines indicating the plugin initialized and registered with EDA.

Step 5: Data Plane#

Continue with chapter 5 Creating the data plane in the Red Hat guide.

EDA Connect requires LLDP on data plane interfaces of compute nodes. On RHOSO 18, model that with an OpenStackDataPlaneService before proceeding beyond step 5.3 "Creating the data plane secrets":

apiVersion: dataplane.openstack.org/v1beta1
kind: OpenStackDataPlaneService
metadata:
  name: edpm-lldp-service
  namespace: openstack
spec:
  edpmServiceType: edpm-lldp-service
  addCertMounts: false
  playbookContents: |
    ---
    - name: Configure LLDP on EDPM compute nodes
      hosts: all
      become: true
      tasks:
        - name: Install lldpd package
          ansible.builtin.package:
            name: lldpd
            state: present

        - name: Enable and start lldpd service
          ansible.builtin.systemd:
            name: lldpd
            state: started
            enabled: true

        - name: Create lldpd config directory
          ansible.builtin.file:
            path: /etc/lldpd.d
            state: directory
            mode: '0755'

        - name: Write persistent lldpd configuration
          ansible.builtin.copy:
            dest: /etc/lldpd.d/eda-connect.conf
            content: |
              configure lldp portidsubtype ifname
              configure system interface pattern em*,en*,p*,!en*v*,!p*v*
            mode: '0644'
          notify: restart lldpd

      handlers:
        - name: restart lldpd
          ansible.builtin.systemd:
            name: lldpd
            state: restarted
oc apply -f - <<'EOF'
apiVersion: dataplane.openstack.org/v1beta1
kind: OpenStackDataPlaneService
metadata:
  name: edpm-lldp-service
  namespace: openstack
spec:
  edpmServiceType: edpm-lldp-service
  addCertMounts: false
  playbookContents: |
    ---
    - name: Configure LLDP on EDPM compute nodes
      hosts: all
      become: true
      tasks:
        - name: Install lldpd package
          ansible.builtin.package:
            name: lldpd
            state: present

        - name: Enable and start lldpd service
          ansible.builtin.systemd:
            name: lldpd
            state: started
            enabled: true

        - name: Create lldpd config directory
          ansible.builtin.file:
            path: /etc/lldpd.d
            state: directory
            mode: '0755'

        - name: Write persistent lldpd configuration
          ansible.builtin.copy:
            dest: /etc/lldpd.d/eda-connect.conf
            content: |
              configure lldp portidsubtype ifname
              configure system interface pattern em*,en*,p*,!en*v*,!p*v*
            mode: '0644'
          notify: restart lldpd

      handlers:
        - name: restart lldpd
          ansible.builtin.systemd:
            name: lldpd
            state: restarted

EOF
Interface pattern

The default pattern em*,en*,p*,!en*v*,!p*v* matches most physical interfaces while excluding common virtual patterns. Override the playbook content if your NIC naming differs.

Step 6: Creating the OpenStackDataPlaneNodeSet#

Define your OpenStackDataPlaneNodeSet following Creating the data plane and, if applicable, Deploying an NFV environment with SR-IOV and DPDK.

Include the previously created edpm-lldp-service in spec.services after the standard services that should run before LLDP (for example after ovn and Neutron metadata services). Exact ordering should follow your validated service list from Red Hat; a typical pattern is to add edpm-lldp-service alongside other post-network services.

Example OpenStackDataPlaneNodeSet YAML

Example only

Node network templates, SR-IOV mappings, repositories, and node addresses are environment-specific. Do not copy values from examples without aligning them to your hardware and RHOSO network design.

apiVersion: dataplane.openstack.org/v1beta1
kind: OpenStackDataPlaneNodeSet
metadata:
  name: openstack-data-plane
  namespace: openstack
spec:
  preProvisioned: true
  networkAttachments:
    - ctlplane
  services:
    - redhat
    - download-cache
    - bootstrap
    - configure-network
    - validate-network
    - install-os
    - configure-os
    - ssh-known-hosts
    - run-os
    - reboot-os
    - install-certs
    - ovn
    - neutron-metadata
    - neutron-sriov         # Include if SR-IOV is used
    - edpm-lldp-service     # EDA: LLDP for topology discovery
    - libvirt
    - nova-custom-sriov     # Include if SR-IOV is used
    - telemetry
  nodeTemplate:
    ansibleSSHPrivateKeySecret: dataplane-ansible-ssh-private-key-secret
    managementNetwork: ctlplane
    ansible:
      ansibleUser: cloud-admin
      ansiblePort: 22
      ansibleVarsFrom:
        - secretRef:
            name: subscription-manager
        - secretRef:
            name: redhat-registry
      ansibleVars:
        rhc_release: "9.4"
        rhc_repositories:
          - {name: "*", state: disabled}
          - {name: "rhel-9-for-x86_64-baseos-eus-rpms", state: enabled}
          - {name: "rhel-9-for-x86_64-appstream-eus-rpms", state: enabled}
          - {name: "rhel-9-for-x86_64-highavailability-eus-rpms", state: enabled}
          - {name: "fast-datapath-for-rhel-9-x86_64-rpms", state: enabled}
          - {name: "rhoso-18.0-for-rhel-9-x86_64-rpms", state: enabled}
          - {name: "rhceph-7-tools-for-rhel-9-x86_64-rpms", state: enabled}
        edpm_bootstrap_release_version_package: []

        # Kernel args required for SR-IOV PCI passthrough (IOMMU)
        edpm_kernel_args: "iommu=pt intel_iommu=on"

        # SR-IOV NIC configuration (adapt interface names and VF counts)
        edpm_neutron_sriov_agent_SRIOV_NIC_physical_device_mappings: "datacentre:ens1f0np0,datacentre:ens1f1np1"
        edpm_nova_libvirt_SRIOV_numvfs:
          ens1f0np0: 8
          ens1f1np1: 8

        # NTP
        edpm_chrony_ntp_servers:
          - <NTP_SERVER_1>
          - <NTP_SERVER_2>

        # Network config (adapt MACs/interfaces to your hardware)
        neutron_physical_bridge_name: br-ex
        edpm_network_config_nmstate: true
        edpm_network_config_update: false
        edpm_network_config_template: |
          ---
          network_config:
          - type: ovs_bridge
            name: {{ neutron_physical_bridge_name }}
            use_dhcp: false
            addresses:
            - ip_netmask: {{ ctlplane_ip }}/{{ ctlplane_cidr }}
            members:
            - type: linux_bond
              name: bond0
              bonding_options: "mode=802.3ad lacp_rate=fast updelay=1000 miimon=100 xmit_hash_policy=layer3+4"
              members:
              - type: interface
                name: nic1
                primary: true
              - type: interface
                name: nic2
            {% for network in nodeset_networks %}
            - type: vlan
              vlan_id: {{ lookup('vars', networks_lower[network] ~ '_vlan_id') }}
              addresses:
              - ip_netmask: {{ lookup('vars', networks_lower[network] ~ '_ip') }}/{{ lookup('vars', networks_lower[network] ~ '_cidr') }}
            {% endfor %}

  nodes:
    edpm-compute-0:
      hostName: edpm-compute-0
      ansible:
        ansibleHost: <COMPUTE_0_CTLPLANE_IP>
      networks:
        - name: ctlplane
          subnetName: subnet1
          fixedIP: <COMPUTE_0_CTLPLANE_IP>
        - name: internalapi
          subnetName: subnet1
        - name: storage
          subnetName: subnet1
        - name: tenant
          subnetName: subnet1
    edpm-compute-1:
      hostName: edpm-compute-1
      ansible:
        ansibleHost: <COMPUTE_1_CTLPLANE_IP>
      networks:
        - name: ctlplane
          subnetName: subnet1
          fixedIP: <COMPUTE_1_CTLPLANE_IP>
        - name: internalapi
          subnetName: subnet1
        - name: storage
          subnetName: subnet1
        - name: tenant
          subnetName: subnet1

Apply the nodeset as normal and wait for it to complete.

Step 7: Deploy the Data Plane#

After the nodeset is deployed, continue deploying the data plane following the steps in Deploying the data plane in the Red Hat guide.

Post-Installation Configuration#

Verify the EDA mechanism driver in Neutron#

NEUTRON_POD=$(oc get pods -n openstack -l service=neutron -o jsonpath='{.items[0].metadata.name}')
oc logs ${NEUTRON_POD} -n openstack | grep -iE "eda_connect|eda connect"

You should see log lines indicating the EDA Connect mechanism driver initialized and registered with EDA.

Verify the EDA CLI extension (openstackclient)#

oc rsh -n openstack "$(oc get pod -n openstack -l app=openstackclient -o jsonpath='{.items[0].metadata.name}')" \
  openstack eda interface mapping --help

Verify LLDP on EDPM nodes#

On a compute node (SSH as your EDPM user, for example cloud-admin):

sudo systemctl status lldpd
sudo lldpcli show neighbors

Verify topology discovery#

From a host or pod where the OpenStack CLI is configured with credentials for the RHOSO cloud (including the openstackclient pod if you use the Nokia client image there):

openstack eda interface mapping list

Verify registration in EDA#

On the EDA cluster:

kubectl get connectplugins -n <eda-namespace>

Troubleshooting#

Neutron pods fail or restart#

oc describe pod -n openstack -l service=neutron
oc logs -n openstack -l service=neutron --tail=200 --previous

Common causes:

  • Invalid or expired API token: Recreate the nokia-eda-ml2-config secret with an updated ML2 fragment, then restart Neutron API pods so they load the new secret (for example oc delete pod -n openstack -l service=neutron, or oc rollout restart on the Neutron API workload if your RHOSO revision exposes it as a Deployment).
  • Unreachable EDA API: Confirm routes and DNS from OpenShift worker nodes to the host in api_host.
  • TLS errors: Confirm eda-ca-secret matches ca_cert_path and the mount path in extraMounts.

LLDP does not show fabric neighbors#

  1. Confirm lldpd is running on the EDPM node.
  2. Inspect /etc/lldpd.d/eda-connect.conf and adjust the interface pattern if NIC names do not match.
  3. Confirm LLDP is enabled on the fabric ports toward the servers.

VLAN networks do not create BridgeDomain objects in EDA#

  • Networks must use provider-network-type vlan for EDA orchestration.
  • Check Neutron logs for EDA-related errors.
  • In EDA: kubectl get connectplugins -n <eda-namespace> -o yaml and verify status.

SR-IOV kernel arguments not active#

If IOMMU flags are not present in /proc/cmdline after deployment, run a data plane deployment that includes the reboot-os service (see Red Hat EDPM documentation) so nodes reboot into the configured kernel.

Reference#