From 993bcb1563bbf6c5bbd5b8497e4d8a313cc4adc4 Mon Sep 17 00:00:00 2001 From: Miro Tomaska Date: Wed, 24 Jun 2026 12:50:45 -0400 Subject: [PATCH 1/3] Move molecule test to zuul in preparation to run add new tests which can not be run in a podman container --- .github/workflows/molecule.yaml | 1 - .../bgp_custom_interconnect_bridge/verify.yml | 1 + .../molecule/bgp_disabled/molecule.yml | 30 +++++------ .../molecule/bgp_disabled/verify.yml | 1 + .../molecule/bgp_enabled/verify.yml | 1 + .../molecule/common/cleanup.yml | 50 +++++++++++++++++++ .../molecule/default/molecule.yml | 33 ++++++------ .../molecule/default/prepare.yml | 39 ++++++++++++--- .../default/tests/test_neutron_ovn.py | 2 +- scripts/test_roles.py | 1 + zuul.d/jobs.yaml | 7 +++ zuul.d/projects.yaml | 1 + 12 files changed, 127 insertions(+), 40 deletions(-) create mode 100644 roles/edpm_neutron_ovn/molecule/common/cleanup.yml diff --git a/.github/workflows/molecule.yaml b/.github/workflows/molecule.yaml index b63ee05fd..ada827b90 100644 --- a/.github/workflows/molecule.yaml +++ b/.github/workflows/molecule.yaml @@ -33,7 +33,6 @@ jobs: - edpm_network_config - edpm_neutron_dhcp - edpm_neutron_metadata - - edpm_neutron_ovn - edpm_neutron_sriov - edpm_nftables - edpm_nodes_validation diff --git a/roles/edpm_neutron_ovn/molecule/bgp_custom_interconnect_bridge/verify.yml b/roles/edpm_neutron_ovn/molecule/bgp_custom_interconnect_bridge/verify.yml index 19911aa3e..3d1b75ffa 100644 --- a/roles/edpm_neutron_ovn/molecule/bgp_custom_interconnect_bridge/verify.yml +++ b/roles/edpm_neutron_ovn/molecule/bgp_custom_interconnect_bridge/verify.yml @@ -2,6 +2,7 @@ - name: Verify hosts: all gather_facts: false + become: true tasks: - name: Verify neutron-bgp-peer-bridges ansible.builtin.shell: > diff --git a/roles/edpm_neutron_ovn/molecule/bgp_disabled/molecule.yml b/roles/edpm_neutron_ovn/molecule/bgp_disabled/molecule.yml index 6b80aa9de..596f9799a 100644 --- a/roles/edpm_neutron_ovn/molecule/bgp_disabled/molecule.yml +++ b/roles/edpm_neutron_ovn/molecule/bgp_disabled/molecule.yml @@ -4,26 +4,25 @@ dependency: options: role-file: collections.yml driver: - name: podman + name: delegated + options: + managed: false + ansible_connection_options: + ansible_connection: local platforms: -- command: /sbin/init - dockerfile: ../../../../molecule/common/Containerfile.j2 - image: ${EDPM_ANSIBLE_MOLECULE_IMAGE:-"ubi9/ubi-init"} - name: instance - privileged: true - registry: - url: ${EDPM_ANSIBLE_MOLECULE_REGISTRY:-"registry.access.redhat.com"} - ulimits: - - host + - name: compute-1 + groups: + - compute provisioner: log: true name: ansible + playbooks: + cleanup: ../common/cleanup.yml inventory: - hosts: - all: - hosts: - instance: - canonical_hostname: edpm-0.localdomain + host_vars: + compute-1: + ctlplane_ip: "10.0.0.1" + canonical_hostname: edpm-0.localdomain scenario: test_sequence: - dependency @@ -32,6 +31,7 @@ scenario: - prepare - converge - verify + - cleanup - destroy verifier: name: ansible diff --git a/roles/edpm_neutron_ovn/molecule/bgp_disabled/verify.yml b/roles/edpm_neutron_ovn/molecule/bgp_disabled/verify.yml index 7e4d98f10..d41acf201 100644 --- a/roles/edpm_neutron_ovn/molecule/bgp_disabled/verify.yml +++ b/roles/edpm_neutron_ovn/molecule/bgp_disabled/verify.yml @@ -2,6 +2,7 @@ - name: Verify hosts: all gather_facts: false + become: true tasks: - name: Check neutron-bgp-peer-bridges is not set ansible.builtin.shell: > diff --git a/roles/edpm_neutron_ovn/molecule/bgp_enabled/verify.yml b/roles/edpm_neutron_ovn/molecule/bgp_enabled/verify.yml index 3043dfd55..c35dfa35e 100644 --- a/roles/edpm_neutron_ovn/molecule/bgp_enabled/verify.yml +++ b/roles/edpm_neutron_ovn/molecule/bgp_enabled/verify.yml @@ -2,6 +2,7 @@ - name: Verify hosts: all gather_facts: false + become: true tasks: - name: Verify neutron-bgp-peer-bridges ansible.builtin.shell: > diff --git a/roles/edpm_neutron_ovn/molecule/common/cleanup.yml b/roles/edpm_neutron_ovn/molecule/common/cleanup.yml new file mode 100644 index 000000000..7286cfdaf --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/common/cleanup.yml @@ -0,0 +1,50 @@ +--- +# Copyright 2026 Red Hat, LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +- name: Cleanup after edpm_neutron_ovn molecule scenario + hosts: all + gather_facts: false + become: true + tasks: + - name: Stop ovn_agent systemd service + ansible.builtin.systemd: + name: edpm_ovn_agent.service + state: stopped + failed_when: false + + - name: Remove ovn_agent container + ansible.builtin.command: + cmd: /usr/bin/podman rm -f ovn_agent + register: _edpm_neutron_ovn_cleanup_ovn_agent + changed_when: _edpm_neutron_ovn_cleanup_ovn_agent.rc == 0 + failed_when: false + + - name: Remove neutron-haproxy sidecar containers + ansible.builtin.shell: | + set -o pipefail + /usr/bin/podman ps -a --filter name=neutron-haproxy- --format '{{ '{{' }}.ID{{ '}}' }}' | + xargs -r /usr/bin/podman rm -f + changed_when: false + failed_when: false + + - name: Clear BGP and EVPN external IDs from Open_vSwitch + ansible.builtin.command: + cmd: >- + ovs-vsctl --if-exists remove Open_vSwitch . external_ids + neutron-bgp-peer-bridges + neutron-bgp-interconnect-bridge + changed_when: false + failed_when: false diff --git a/roles/edpm_neutron_ovn/molecule/default/molecule.yml b/roles/edpm_neutron_ovn/molecule/default/molecule.yml index 66f115535..409e50cdd 100644 --- a/roles/edpm_neutron_ovn/molecule/default/molecule.yml +++ b/roles/edpm_neutron_ovn/molecule/default/molecule.yml @@ -4,26 +4,28 @@ dependency: options: role-file: collections.yml driver: - name: podman + name: delegated + options: + managed: false + ansible_connection_options: + ansible_connection: local platforms: -- command: /sbin/init - dockerfile: ../../../../molecule/common/Containerfile.j2 - image: ${EDPM_ANSIBLE_MOLECULE_IMAGE:-"ubi9/ubi-init"} - name: instance - privileged: true - registry: - url: ${EDPM_ANSIBLE_MOLECULE_REGISTRY:-"registry.access.redhat.com"} - ulimits: - - host + - name: compute-1 + groups: + - compute provisioner: log: true name: ansible + playbooks: + cleanup: ../common/cleanup.yml inventory: - hosts: - all: - hosts: - instance: - canonical_hostname: edpm-0.localdomain + host_vars: + compute-1: + ctlplane_ip: "10.0.0.1" + canonical_hostname: edpm-0.localdomain + # Containers and config under /var/lib/ are managed as root via + # systemd; testinfra must escalate to inspect podman and host files. + ansible_become: true scenario: test_sequence: - dependency @@ -32,6 +34,7 @@ scenario: - prepare - converge - verify + - cleanup - destroy verifier: name: testinfra diff --git a/roles/edpm_neutron_ovn/molecule/default/prepare.yml b/roles/edpm_neutron_ovn/molecule/default/prepare.yml index 9141f2ba2..1e7bc82ec 100644 --- a/roles/edpm_neutron_ovn/molecule/default/prepare.yml +++ b/roles/edpm_neutron_ovn/molecule/default/prepare.yml @@ -24,19 +24,42 @@ - openvswitch - iproute - podman + - name: Prepare hosts: all gather_facts: false + vars: + edpm_download_cache_podman_auth_file: "" tasks: - ansible.builtin.include_role: name: osp.edpm.env_data - # The openvswitch kernel module needs to be loaded on the host - - name: install and modprobe openvswitch - shell: | - sudo dnf -y install openvswitch - sudo modprobe openvswitch - delegate_to: localhost - run_once: true + - name: Install and modprobe openvswitch + ansible.builtin.shell: | + set -eux + dnf -y install openvswitch + modprobe openvswitch + become: true + changed_when: true + + - name: Force systemd to reread configs + ansible.builtin.systemd: + daemon_reload: true + become: true + + - name: Download required role packages + ansible.builtin.import_role: + name: osp.edpm.edpm_bootstrap + tasks_from: download_cache.yml + + - name: Include edpm_bootstrap + ansible.builtin.import_role: + name: osp.edpm.edpm_bootstrap + tasks_from: bootstrap.yml + become: true - post_tasks: [] + - name: Download edpm_neutron_ovn container images + ansible.builtin.import_role: + name: osp.edpm.edpm_neutron_ovn + tasks_from: download_cache.yml + become: true diff --git a/roles/edpm_neutron_ovn/molecule/default/tests/test_neutron_ovn.py b/roles/edpm_neutron_ovn/molecule/default/tests/test_neutron_ovn.py index a04ea290b..50d552dfb 100644 --- a/roles/edpm_neutron_ovn/molecule/default/tests/test_neutron_ovn.py +++ b/roles/edpm_neutron_ovn/molecule/default/tests/test_neutron_ovn.py @@ -64,7 +64,7 @@ class TestNeutronOvn(unittest.TestCase): def setUp(self): self.host = testinfra.get_host( - "ansible://instance?ansible_inventory=%s" % + "ansible://compute-1?ansible_inventory=%s" % os.environ['MOLECULE_INVENTORY_FILE']) def tearDown(self): diff --git a/scripts/test_roles.py b/scripts/test_roles.py index d9ff971e9..ac602f230 100755 --- a/scripts/test_roles.py +++ b/scripts/test_roles.py @@ -19,6 +19,7 @@ "edpm_frr", "edpm_kernel", "edpm_ovn_bgp_agent", + "edpm_neutron_ovn", "test_deps", ] diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml index b11274043..dda4b227b 100644 --- a/zuul.d/jobs.yaml +++ b/zuul.d/jobs.yaml @@ -96,6 +96,13 @@ - ^roles/edpm_ovn_bgp_agent/(defaults|files|handlers|library|lookup_plugins|module_utils|molecule|tasks|templates|vars).* vars: TEST_RUN: edpm_ovn_bgp_agent +- job: + name: edpm-ansible-molecule-edpm_neutron_ovn + parent: edpm-ansible-molecule-base + files: + - ^roles/edpm_neutron_ovn/(defaults|files|handlers|library|lookup_plugins|module_utils|molecule|tasks|templates|vars).* + vars: + TEST_RUN: edpm_neutron_ovn - job: name: edpm-ansible-molecule-edpm_ovs parent: edpm-ansible-molecule-base diff --git a/zuul.d/projects.yaml b/zuul.d/projects.yaml index e729508cf..ae251e794 100644 --- a/zuul.d/projects.yaml +++ b/zuul.d/projects.yaml @@ -21,6 +21,7 @@ - edpm-ansible-molecule-edpm_iscsid - edpm-ansible-molecule-edpm_multipathd - edpm-ansible-molecule-edpm_ovn_bgp_agent + - edpm-ansible-molecule-edpm_neutron_ovn - edpm-ansible-molecule-edpm_ovs - edpm-ansible-molecule-edpm_tripleo_cleanup - edpm-ansible-molecule-edpm_tuned From c4b0732a7159eb07f4578eefd536b93a199abd00 Mon Sep 17 00:00:00 2001 From: Miro Tomaska Date: Thu, 11 Jun 2026 13:16:47 -0400 Subject: [PATCH 2/3] Add ovn agent evpn extension config variables Sets `bgp_as`, `bgp_local_interface`, `child_vxlan_port` and `frr_vty_socket` under [ovn_evpn] section for the ovn-agent config. It sets Open_vswitch table external ids required by OVN agent evpn extension. Lastly, it loads vrf kernel module needed for frr service --- roles/edpm_neutron_ovn/defaults/main.yml | 9 ++++ .../edpm_neutron_ovn/meta/argument_specs.yml | 39 +++++++++++++++ .../molecule/evpn_disabled/collections.yml | 3 ++ .../molecule/evpn_disabled/converge.yml | 28 +++++++++++ .../molecule/evpn_disabled/molecule.yml | 1 + .../molecule/evpn_disabled/prepare.yml | 1 + .../test-data/10-neutron-ovn.conf | 3 ++ .../molecule/evpn_disabled/verify.yml | 30 ++++++++++++ .../molecule/evpn_enabled/collections.yml | 3 ++ .../molecule/evpn_enabled/converge.yml | 32 +++++++++++++ .../molecule/evpn_enabled/molecule.yml | 37 +++++++++++++++ .../molecule/evpn_enabled/prepare.yml | 1 + .../test-data/10-neutron-ovn.conf | 3 ++ .../molecule/evpn_enabled/verify.yml | 47 +++++++++++++++++++ roles/edpm_neutron_ovn/tasks/configure.yml | 34 ++++++++++++++ .../config/neutron-ovn-agent.conf.j2 | 8 ++++ 16 files changed, 279 insertions(+) create mode 100644 roles/edpm_neutron_ovn/molecule/evpn_disabled/collections.yml create mode 100644 roles/edpm_neutron_ovn/molecule/evpn_disabled/converge.yml create mode 120000 roles/edpm_neutron_ovn/molecule/evpn_disabled/molecule.yml create mode 120000 roles/edpm_neutron_ovn/molecule/evpn_disabled/prepare.yml create mode 100644 roles/edpm_neutron_ovn/molecule/evpn_disabled/test-data/10-neutron-ovn.conf create mode 100644 roles/edpm_neutron_ovn/molecule/evpn_disabled/verify.yml create mode 100644 roles/edpm_neutron_ovn/molecule/evpn_enabled/collections.yml create mode 100644 roles/edpm_neutron_ovn/molecule/evpn_enabled/converge.yml create mode 100644 roles/edpm_neutron_ovn/molecule/evpn_enabled/molecule.yml create mode 120000 roles/edpm_neutron_ovn/molecule/evpn_enabled/prepare.yml create mode 100644 roles/edpm_neutron_ovn/molecule/evpn_enabled/test-data/10-neutron-ovn.conf create mode 100644 roles/edpm_neutron_ovn/molecule/evpn_enabled/verify.yml diff --git a/roles/edpm_neutron_ovn/defaults/main.yml b/roles/edpm_neutron_ovn/defaults/main.yml index 4486a6761..f0b013699 100644 --- a/roles/edpm_neutron_ovn/defaults/main.yml +++ b/roles/edpm_neutron_ovn/defaults/main.yml @@ -70,3 +70,12 @@ edpm_neutron_ovn_agent_bgp_peer_bridges: '' # BGP interconnect bridge for the ovn-bgp extension. # Set this variable when the ovn-agent is configured with the ovn-bgp extension. edpm_neutron_ovn_agent_bgp_interconnect_bridge: "{{ neutron_physical_bridge_name | default('br-ex') }}" + +# ovn-evpn extension options. Set edpm_neutron_ovn_agent_agent_extensions to include +# 'ovn-evpn' to load the extension and render the [ovn_evpn] section. +edpm_neutron_ovn_agent_ovn_evpn_bgp_as: '' +edpm_neutron_ovn_agent_ovn_evpn_bgp_local_interface: '' +edpm_neutron_ovn_agent_ovn_evpn_child_vxlan_port: '49152' +edpm_neutron_ovn_agent_frr_vty_socket: '/run/frr' +edpm_neutron_ovn_evpn_local_ip: '' +edpm_neutron_ovn_evpn_vxlan_port: 4789 diff --git a/roles/edpm_neutron_ovn/meta/argument_specs.yml b/roles/edpm_neutron_ovn/meta/argument_specs.yml index d26f45b6d..2df9caea7 100644 --- a/roles/edpm_neutron_ovn/meta/argument_specs.yml +++ b/roles/edpm_neutron_ovn/meta/argument_specs.yml @@ -117,3 +117,42 @@ argument_specs: Defaults to neutron_physical_bridge_name (br-ex if unset). Set this variable when the ovn-agent is configured with the ovn-bgp extension. type: str + edpm_neutron_ovn_agent_ovn_evpn_bgp_as: + default: '' + description: > + 'BGP Autonomous System number for EVPN. + Used when edpm_neutron_ovn_agent_agent_extensions includes 'ovn-evpn'. + type: str + edpm_neutron_ovn_agent_ovn_evpn_bgp_local_interface: + default: '' + description: > + The local interface name (e.g. eth2) on which to establish BGP peer session. + Used when edpm_neutron_ovn_agent_agent_extensions includes 'ovn-evpn'. + type: str + edpm_neutron_ovn_agent_ovn_evpn_child_vxlan_port: + default: '49152' + description: > + UDP port for the child VxLAN device used by EVPN. + Used when edpm_neutron_ovn_agent_agent_extensions includes 'ovn-evpn'. + This port is only used between the vxlan device and the vtep and not for any encapsulation. + type: str + edpm_neutron_ovn_agent_frr_vty_socket: + default: '/run/frr' + description: > + Path to the vtysh socket directory. Passed as --vty_socket to the vtysh command. + Used when edpm_neutron_ovn_agent_agent_extensions includes 'ovn-evpn'. + type: str + edpm_neutron_ovn_evpn_local_ip: + default: '' + description: > + IP address written to OVS external-ids:ovn-evpn-local-ip, used as the source + for EVPN traffic egressing through the VXLAN tunnel. + When empty, defaults to the BGP IPv4 source network IP on the host. + type: str + edpm_neutron_ovn_evpn_vxlan_port: + default: 4789 + description: > + UDP port written to OVS external-ids:ovn-evpn-vxlan-ports. + Used when edpm_neutron_ovn_agent_agent_extensions includes 'ovn-evpn'. + This port is used by vxlan tunnels used for encapsulation. + type: int diff --git a/roles/edpm_neutron_ovn/molecule/evpn_disabled/collections.yml b/roles/edpm_neutron_ovn/molecule/evpn_disabled/collections.yml new file mode 100644 index 000000000..424ad60b8 --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/evpn_disabled/collections.yml @@ -0,0 +1,3 @@ +--- +collections: +- name: community.general diff --git a/roles/edpm_neutron_ovn/molecule/evpn_disabled/converge.yml b/roles/edpm_neutron_ovn/molecule/evpn_disabled/converge.yml new file mode 100644 index 000000000..e55e0087c --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/evpn_disabled/converge.yml @@ -0,0 +1,28 @@ +--- +# Copyright 2026 Red Hat, LLC +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +- name: Converge + hosts: all + gather_facts: false + become: true + tasks: + - ansible.builtin.include_role: + name: "osp.edpm.edpm_neutron_ovn" + vars: + tenant_ip: "{{ ansible_host }}" + edpm_ovn_dbs: + - "{{ ansible_host }}" + edpm_neutron_ovn_config_src: "{{lookup('env', 'MOLECULE_SCENARIO_DIRECTORY')}}/test-data" diff --git a/roles/edpm_neutron_ovn/molecule/evpn_disabled/molecule.yml b/roles/edpm_neutron_ovn/molecule/evpn_disabled/molecule.yml new file mode 120000 index 000000000..947efe99b --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/evpn_disabled/molecule.yml @@ -0,0 +1 @@ +../evpn_enabled/molecule.yml \ No newline at end of file diff --git a/roles/edpm_neutron_ovn/molecule/evpn_disabled/prepare.yml b/roles/edpm_neutron_ovn/molecule/evpn_disabled/prepare.yml new file mode 120000 index 000000000..1c017d9a2 --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/evpn_disabled/prepare.yml @@ -0,0 +1 @@ +../default/prepare.yml \ No newline at end of file diff --git a/roles/edpm_neutron_ovn/molecule/evpn_disabled/test-data/10-neutron-ovn.conf b/roles/edpm_neutron_ovn/molecule/evpn_disabled/test-data/10-neutron-ovn.conf new file mode 100644 index 000000000..e573ebef4 --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/evpn_disabled/test-data/10-neutron-ovn.conf @@ -0,0 +1,3 @@ +[ovn] +ovn_nb_connection = tcp:6.7.8.9:1233 +ovn_sb_connection = tcp:6.7.8.9:1234 diff --git a/roles/edpm_neutron_ovn/molecule/evpn_disabled/verify.yml b/roles/edpm_neutron_ovn/molecule/evpn_disabled/verify.yml new file mode 100644 index 000000000..cd81db64a --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/evpn_disabled/verify.yml @@ -0,0 +1,30 @@ +--- +- name: Verify + hosts: all + gather_facts: false + become: true + vars: + neutron_ovn_agent_conf: /var/lib/openstack/neutron-ovn-agent/01-neutron-ovn-agent.conf + tasks: + - name: Slurp neutron-ovn-agent config + become: true + ansible.builtin.slurp: + src: "{{ neutron_ovn_agent_conf }}" + register: agent_config + + - name: Assert [ovn_evpn] section is not present + ansible.builtin.assert: + that: + - "'[ovn_evpn]' not in agent_config.content | b64decode" + + - name: Check ovn-evpn-local-ip is not set + ansible.builtin.shell: > + /usr/bin/ovs-vsctl --if-exists get open_vswitch . external_ids:ovn-evpn-local-ip + register: output + failed_when: output.stdout != '' + + - name: Check ovn-evpn-vxlan-ports is not set + ansible.builtin.shell: > + /usr/bin/ovs-vsctl --if-exists get open_vswitch . external_ids:ovn-evpn-vxlan-ports + register: output + failed_when: output.stdout != '' diff --git a/roles/edpm_neutron_ovn/molecule/evpn_enabled/collections.yml b/roles/edpm_neutron_ovn/molecule/evpn_enabled/collections.yml new file mode 100644 index 000000000..424ad60b8 --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/evpn_enabled/collections.yml @@ -0,0 +1,3 @@ +--- +collections: +- name: community.general diff --git a/roles/edpm_neutron_ovn/molecule/evpn_enabled/converge.yml b/roles/edpm_neutron_ovn/molecule/evpn_enabled/converge.yml new file mode 100644 index 000000000..bc877461d --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/evpn_enabled/converge.yml @@ -0,0 +1,32 @@ +--- +# Copyright 2026 Red Hat, LLC +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +- name: Converge + hosts: all + gather_facts: false + become: true + tasks: + - ansible.builtin.include_role: + name: "osp.edpm.edpm_neutron_ovn" + vars: + tenant_ip: "{{ ansible_host }}" + edpm_ovn_dbs: + - "{{ ansible_host }}" + edpm_neutron_ovn_config_src: "{{lookup('env', 'MOLECULE_SCENARIO_DIRECTORY')}}/test-data" + edpm_neutron_ovn_agent_agent_extensions: ovn-evpn + edpm_neutron_ovn_agent_ovn_evpn_bgp_as: '64999' + edpm_neutron_ovn_agent_ovn_evpn_bgp_local_interface: eth0 + edpm_neutron_ovn_agent_ovn_evpn_child_vxlan_port: '49152' diff --git a/roles/edpm_neutron_ovn/molecule/evpn_enabled/molecule.yml b/roles/edpm_neutron_ovn/molecule/evpn_enabled/molecule.yml new file mode 100644 index 000000000..596f9799a --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/evpn_enabled/molecule.yml @@ -0,0 +1,37 @@ +--- +dependency: + name: galaxy + options: + role-file: collections.yml +driver: + name: delegated + options: + managed: false + ansible_connection_options: + ansible_connection: local +platforms: + - name: compute-1 + groups: + - compute +provisioner: + log: true + name: ansible + playbooks: + cleanup: ../common/cleanup.yml + inventory: + host_vars: + compute-1: + ctlplane_ip: "10.0.0.1" + canonical_hostname: edpm-0.localdomain +scenario: + test_sequence: + - dependency + - destroy + - create + - prepare + - converge + - verify + - cleanup + - destroy +verifier: + name: ansible diff --git a/roles/edpm_neutron_ovn/molecule/evpn_enabled/prepare.yml b/roles/edpm_neutron_ovn/molecule/evpn_enabled/prepare.yml new file mode 120000 index 000000000..1c017d9a2 --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/evpn_enabled/prepare.yml @@ -0,0 +1 @@ +../default/prepare.yml \ No newline at end of file diff --git a/roles/edpm_neutron_ovn/molecule/evpn_enabled/test-data/10-neutron-ovn.conf b/roles/edpm_neutron_ovn/molecule/evpn_enabled/test-data/10-neutron-ovn.conf new file mode 100644 index 000000000..e573ebef4 --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/evpn_enabled/test-data/10-neutron-ovn.conf @@ -0,0 +1,3 @@ +[ovn] +ovn_nb_connection = tcp:6.7.8.9:1233 +ovn_sb_connection = tcp:6.7.8.9:1234 diff --git a/roles/edpm_neutron_ovn/molecule/evpn_enabled/verify.yml b/roles/edpm_neutron_ovn/molecule/evpn_enabled/verify.yml new file mode 100644 index 000000000..fe9c890b9 --- /dev/null +++ b/roles/edpm_neutron_ovn/molecule/evpn_enabled/verify.yml @@ -0,0 +1,47 @@ +--- +- name: Verify + hosts: all + gather_facts: false + become: true + vars: + neutron_ovn_agent_conf: /var/lib/openstack/neutron-ovn-agent/01-neutron-ovn-agent.conf + tasks: + - name: Slurp neutron-ovn-agent config + become: true + ansible.builtin.slurp: + src: "{{ neutron_ovn_agent_conf }}" + register: agent_config + + - name: Assert [ovn_evpn] section is present with expected values + ansible.builtin.assert: + that: + - "'[ovn_evpn]' in agent_config.content | b64decode" + - "'bgp_as = 64999' in agent_config.content | b64decode" + - "'bgp_local_interface = eth0' in agent_config.content | b64decode" + - "'child_vxlan_port = 49152' in agent_config.content | b64decode" + - "'frr_vty_socket = /run/frr' in agent_config.content | b64decode" + - "'extensions = ovn-evpn' in agent_config.content | b64decode" + + - name: Verify vrf module is loaded + become: true + ansible.builtin.command: grep -q '^vrf ' /proc/modules + changed_when: false + + - name: Verify vrf module persistence config exists + become: true + ansible.builtin.stat: + path: /etc/modules-load.d/vrf.conf + register: vrf_conf + failed_when: not vrf_conf.stat.exists + + - name: Verify ovn-evpn-local-ip defaults to ctlplane_ip + ansible.builtin.shell: > + /usr/bin/ovs-vsctl get open_vswitch . external_ids:ovn-evpn-local-ip + register: output + failed_when: output.stdout != '"10.0.0.1"' + + - name: Verify ovn-evpn-vxlan-ports defaults to 4789 + ansible.builtin.shell: > + /usr/bin/ovs-vsctl get open_vswitch . external_ids:ovn-evpn-vxlan-ports + register: output + failed_when: output.stdout != '"4789"' diff --git a/roles/edpm_neutron_ovn/tasks/configure.yml b/roles/edpm_neutron_ovn/tasks/configure.yml index ccc7ecb81..9b4563546 100644 --- a/roles/edpm_neutron_ovn/tasks/configure.yml +++ b/roles/edpm_neutron_ovn/tasks/configure.yml @@ -68,3 +68,37 @@ # set when BGP is enabled, so it is used as the gate for this task. when: edpm_neutron_ovn_agent_bgp_peer_bridges != '' changed_when: true + + - name: EVPN extension configuration + when: "'ovn-evpn' in edpm_neutron_ovn_agent_agent_extensions.split(',') | map('trim') | list" + block: + - name: Gather ansible_local facts + ansible.builtin.setup: + gather_subset: + - "!all" + - "!min" + - "local" + when: + - ansible_local is not defined + + - name: Load vrf kernel module + ansible.builtin.import_role: + name: osp.edpm.edpm_module_load + vars: + modules: + - name: vrf + + - name: Set effective EVPN local IP + ansible.builtin.set_fact: + _edpm_neutron_ovn_evpn_local_ip_effective: >- + {{ edpm_neutron_ovn_evpn_local_ip if edpm_neutron_ovn_evpn_local_ip | length > 0 + else hostvars[inventory_hostname][(edpm_frr_bgp_ipv4_src_network | default('ctlplane')) ~ '_ip'] }} + + - name: Set EVPN external-ids in OVS + become: true + ansible.builtin.command: + cmd: >- + ovs-vsctl --no-wait -- set Open_vSwitch . + external-ids:ovn-evpn-local-ip={{ _edpm_neutron_ovn_evpn_local_ip_effective }} + external-ids:ovn-evpn-vxlan-ports={{ edpm_neutron_ovn_evpn_vxlan_port }} + changed_when: true diff --git a/roles/edpm_neutron_ovn/templates/config/neutron-ovn-agent.conf.j2 b/roles/edpm_neutron_ovn/templates/config/neutron-ovn-agent.conf.j2 index 371fe3e25..227aebf02 100644 --- a/roles/edpm_neutron_ovn/templates/config/neutron-ovn-agent.conf.j2 +++ b/roles/edpm_neutron_ovn/templates/config/neutron-ovn-agent.conf.j2 @@ -15,3 +15,11 @@ ovsdb_connection_timeout = {{ edpm_neutron_ovn_agent_ovn_ovsdb_connection_timeou ovsdb_probe_interval = {{ edpm_neutron_ovn_agent_ovn_ovsdb_probe_interval }} ovn_nb_connection = {{ edpm_neutron_ovn_agent_ovn_ovn_nb_connection }} ovn_sb_connection = {{ edpm_neutron_ovn_agent_ovn_ovn_sb_connection }} +{% if 'ovn-evpn' in edpm_neutron_ovn_agent_agent_extensions.split(',') | map('trim') | list %} + +[ovn_evpn] +bgp_as = {{ edpm_neutron_ovn_agent_ovn_evpn_bgp_as }} +bgp_local_interface = {{ edpm_neutron_ovn_agent_ovn_evpn_bgp_local_interface }} +child_vxlan_port = {{ edpm_neutron_ovn_agent_ovn_evpn_child_vxlan_port }} +frr_vty_socket = {{ edpm_neutron_ovn_agent_frr_vty_socket }} +{% endif %} From 14bae9948440f3aea5e20111bc8e8269cb1dbf27 Mon Sep 17 00:00:00 2001 From: Miro Tomaska Date: Tue, 16 Jun 2026 16:25:27 -0400 Subject: [PATCH 3/3] Ovn-agent needs access to /run/frr to get access to vtysh socket --- roles/edpm_neutron_ovn/defaults/main.yml | 1 + .../edpm_neutron_ovn/meta/argument_specs.yml | 1 + .../molecule/default/prepare.yml | 42 ++++++++++++++++++- .../molecule/evpn_enabled/verify.yml | 14 +++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/roles/edpm_neutron_ovn/defaults/main.yml b/roles/edpm_neutron_ovn/defaults/main.yml index f0b013699..a83115cec 100644 --- a/roles/edpm_neutron_ovn/defaults/main.yml +++ b/roles/edpm_neutron_ovn/defaults/main.yml @@ -24,6 +24,7 @@ edpm_neutron_ovn_common_volumes: - "{{ edpm_neutron_ovn_agent_lib_dir }}:/var/lib/neutron:shared,z" - "{{ edpm_neutron_ovn_agent_lib_dir }}/ovn_agent_haproxy_wrapper:/usr/local/bin/haproxy:ro" - "{{ edpm_neutron_ovn_agent_lib_dir }}/kill_scripts:/etc/neutron/kill_scripts:ro" + - /run/frr:/run/frr:shared,z # Sidecar containers settings edpm_neutron_ovn_agent_sidecar_debug: false diff --git a/roles/edpm_neutron_ovn/meta/argument_specs.yml b/roles/edpm_neutron_ovn/meta/argument_specs.yml index 2df9caea7..fab98a1c3 100644 --- a/roles/edpm_neutron_ovn/meta/argument_specs.yml +++ b/roles/edpm_neutron_ovn/meta/argument_specs.yml @@ -85,6 +85,7 @@ argument_specs: - /run/openvswitch:/run/openvswitch:z - '{{ edpm_neutron_ovn_agent_config_dir }}:/etc/neutron.conf.d:z' - /var/lib/kolla/config_files/ovn_agent.json:/var/lib/kolla/config_files/config.json:ro + - /run/frr:/run/frr:shared,z description: Volume mounts for Neutron OVN agent type: list edpm_neutron_ovn_tls_enabled: diff --git a/roles/edpm_neutron_ovn/molecule/default/prepare.yml b/roles/edpm_neutron_ovn/molecule/default/prepare.yml index 1e7bc82ec..dc5cf1606 100644 --- a/roles/edpm_neutron_ovn/molecule/default/prepare.yml +++ b/roles/edpm_neutron_ovn/molecule/default/prepare.yml @@ -1,5 +1,5 @@ --- -# Copyright 2023 Red Hat, Inc. +# Copyright 2026 Red Hat, LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -63,3 +63,43 @@ name: osp.edpm.edpm_neutron_ovn tasks_from: download_cache.yml become: true + + - name: Install os-net-config + ansible.builtin.package: + name: os-net-config + state: present + become: true + + - name: Download edpm_frr container images + ansible.builtin.include_role: + name: osp.edpm.edpm_frr + tasks_from: download_cache.yml + + - name: Create firewall directory + become: true + ansible.builtin.file: + path: /var/lib/edpm-config/firewall + state: directory + owner: root + group: root + mode: "0750" + + - name: open port 22 (edpm_nftables will active this later) + become: true + ansible.builtin.copy: + dest: /var/lib/edpm-config/firewall/sshd-networks.yaml + content: | + - rule_name: 003 Allow SSH + rule: + proto: tcp + dport: 22 + + - name: Running FRR + vars: + edpm_frr_bgp_peers: + - 10.64.0.1 + - 10.65.0.1 + ansible.builtin.include_role: + name: osp.edpm.edpm_frr + apply: + become: true diff --git a/roles/edpm_neutron_ovn/molecule/evpn_enabled/verify.yml b/roles/edpm_neutron_ovn/molecule/evpn_enabled/verify.yml index fe9c890b9..f9b665253 100644 --- a/roles/edpm_neutron_ovn/molecule/evpn_enabled/verify.yml +++ b/roles/edpm_neutron_ovn/molecule/evpn_enabled/verify.yml @@ -22,6 +22,20 @@ - "'frr_vty_socket = /run/frr' in agent_config.content | b64decode" - "'extensions = ovn-evpn' in agent_config.content | b64decode" + - name: Collect ovn_agent bind mounts + ansible.builtin.shell: | + /usr/bin/podman inspect --format '{{ '{{' }}.HostConfig.Binds{{ '}}' }}' ovn_agent + register: _edpm_neutron_ovn_ovn_agent_binds + changed_when: false + + - name: Assert /run/frr bind is present on ovn_agent + ansible.builtin.assert: + that: + - "'/run/frr:/run/frr:' in _edpm_neutron_ovn_ovn_agent_binds.stdout" + fail_msg: >- + /run/frr bind not found in ovn_agent HostConfig.Binds + (binds: {{ _edpm_neutron_ovn_ovn_agent_binds.stdout }}) + - name: Verify vrf module is loaded become: true ansible.builtin.command: grep -q '^vrf ' /proc/modules