From 1e96f9b7421808ebb2e91c2668faaf23e4268832 Mon Sep 17 00:00:00 2001 From: Pierre Riteau Date: Tue, 4 Aug 2026 11:11:55 +0200 Subject: [PATCH 1/4] Fix Bifrost extra_kernel_params validation Ironic recently added parsing of extra_kernel_params options [1]. Kayobe was generating "ipa-inspection-benchmarks=" when extra-hardware was disabled, which failed to validate. Update ipa_kernel_options_default to avoid producing kernel parameters with no value. [1] https://review.opendev.org/c/openstack/ironic/+/992306 Closes-Bug: #2162755 Change-Id: I35827eeb132fc7cd75a85aa0b33367d0a56dd33f Signed-off-by: Pierre Riteau (cherry picked from commit 8aae85bb9016579f1240a056e6525ffdc5b19df1) --- ansible/inventory/group_vars/all/ipa | 4 ++-- doc/source/configuration/reference/ironic-python-agent.rst | 7 ++++--- releasenotes/notes/bug-2162755-9852bd6b0606d9d8.yaml | 6 ++++++ 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/bug-2162755-9852bd6b0606d9d8.yaml diff --git a/ansible/inventory/group_vars/all/ipa b/ansible/inventory/group_vars/all/ipa index 0ace49f8f..7b98c3366 100644 --- a/ansible/inventory/group_vars/all/ipa +++ b/ansible/inventory/group_vars/all/ipa @@ -178,8 +178,8 @@ ipa_benchmarks: > # List of default kernel parameters for Ironic python agent. ipa_kernel_options_default: > {{ ['ipa-collect-lldp=' ~ ('1' if ipa_collect_lldp | bool else '0')] + - ['ipa-inspection-collectors=' ~ ipa_collectors | join(',')] + - ['ipa-inspection-benchmarks=' ~ ipa_benchmarks | join(',')] }} + (['ipa-inspection-collectors=' ~ ipa_collectors | join(',')] if ipa_collectors else []) + + (['ipa-inspection-benchmarks=' ~ ipa_benchmarks | join(',')] if ipa_benchmarks else []) }} # List of additional kernel parameters for Ironic python agent. ipa_kernel_options_extra: [] diff --git a/doc/source/configuration/reference/ironic-python-agent.rst b/doc/source/configuration/reference/ironic-python-agent.rst index 6492bb04e..3ece11e96 100644 --- a/doc/source/configuration/reference/ironic-python-agent.rst +++ b/doc/source/configuration/reference/ironic-python-agent.rst @@ -327,9 +327,10 @@ inspection. ``ipa_benchmarks_default`` and ``ipa_benchmarks_extra``. ``ipa_kernel_options_default`` List of default kernel parameters for Ironic python agent. Default includes - ``ipa-collect-lldp``, ``ipa-inspection-collectors`` and - ``ipa-inspection-benchmarks``, with arguments taken from - ``ipa_collect_lldp``, ``ipa_collectors`` and ``ipa_benchmarks``. + ``ipa-collect-lldp``, ``ipa-inspection-collectors`` (if ``ipa_collectors`` + is not empty) and ``ipa-inspection-benchmarks`` (if ``ipa_benchmarks`` is + not empty), with arguments taken from ``ipa_collect_lldp``, + ``ipa_collectors`` and ``ipa_benchmarks``. ``ipa_kernel_options_extra`` List of additional kernel parameters for Ironic python agent. Default is none. diff --git a/releasenotes/notes/bug-2162755-9852bd6b0606d9d8.yaml b/releasenotes/notes/bug-2162755-9852bd6b0606d9d8.yaml new file mode 100644 index 000000000..19a316df3 --- /dev/null +++ b/releasenotes/notes/bug-2162755-9852bd6b0606d9d8.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixes Bifrost deployment by ensuring that extra kernel parameters are + successfully parsed by Ironic. + `LP#2162755 `__ From 2784daaf17b61fffde7eadd68ce92d591be1db3f Mon Sep 17 00:00:00 2001 From: Pierre Riteau Date: Mon, 10 Aug 2026 12:28:20 +0200 Subject: [PATCH 2/4] CI: Use kernel-modules-extra-matched package The installation of the kernel-modules-extra package could fail when running a debug kernel, as is the case for OpenDev CentOS Stream images. Use kernel-modules-extra-matched to install the package matching the running kernel. Change-Id: Ia9652d0e25e6f472706de2b5eda72779a02a42af Signed-off-by: Pierre Riteau (cherry picked from commit bef0e93b4ee707b2904a0746e8c1dc7f7addf3cc) --- roles/kayobe-ci-prep/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/kayobe-ci-prep/tasks/main.yml b/roles/kayobe-ci-prep/tasks/main.yml index 11847e719..cc813b87b 100644 --- a/roles/kayobe-ci-prep/tasks/main.yml +++ b/roles/kayobe-ci-prep/tasks/main.yml @@ -64,7 +64,7 @@ - name: Install kernel-modules-extra for running kernel ansible.builtin.dnf: - name: "kernel-modules-extra-{{ ansible_facts.kernel }}" + name: "kernel-modules-extra-matched" state: present when: ansible_facts.os_family == 'RedHat' From cd86d06ecc1b64f486fae479bff683f129a263b2 Mon Sep 17 00:00:00 2001 From: Bartosz Bezak Date: Wed, 19 Aug 2026 13:02:55 +0200 Subject: [PATCH 3/4] Set MTU on nmstate bridge ports Propagate the bridge MTU to its physical ports when using the nmstate network engine. Closes-Bug: #2164513 Change-Id: I05d3f386157b887d5f550104fef7c5e7ffeed923 Signed-off-by: Bartosz Bezak (cherry picked from commit 4ac2b42dda806375091404fcd1df8e8cc7e7aa26) --- kayobe/plugins/filter/nmstate.py | 2 ++ kayobe/tests/unit/plugins/filter/test_nmstate.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/kayobe/plugins/filter/nmstate.py b/kayobe/plugins/filter/nmstate.py index 7fae395f1..4559207e5 100644 --- a/kayobe/plugins/filter/nmstate.py +++ b/kayobe/plugins/filter/nmstate.py @@ -519,6 +519,8 @@ def get_iface(name): # _port_type_. for port in br_ports or []: port_iface = get_iface(port) + if mtu: + port_iface.setdefault("mtu", mtu) if "type" not in port_iface: # Check for explicit type configuration port_type = networks.net_attr( diff --git a/kayobe/tests/unit/plugins/filter/test_nmstate.py b/kayobe/tests/unit/plugins/filter/test_nmstate.py index da1fef140..d1869d866 100644 --- a/kayobe/tests/unit/plugins/filter/test_nmstate.py +++ b/kayobe/tests/unit/plugins/filter/test_nmstate.py @@ -44,6 +44,7 @@ class TestNMStateFilter(unittest.TestCase): "net3_interface": "br0", "net3_bridge_ports": ['eth1'], "net3_bridge_stp": True, + "net3_mtu": 9000, # net4: bond on bond0 with slaves eth2 and eth3. "net4_interface": "bond0", "net4_bond_slaves": ['eth2', 'eth3'], @@ -123,6 +124,7 @@ def test_nmstate_config_bridge(self): result = nmstate.nmstate_config(self.context, ["net3"]) br_iface = next(i for i in result["interfaces"] if i["name"] == "br0") self.assertEqual(br_iface["type"], "linux-bridge") + self.assertEqual(br_iface["mtu"], 9000) self.assertEqual(br_iface["bridge"]["port"], [{"name": "eth1"}]) self.assertTrue(br_iface["bridge"]["options"]["stp"]["enabled"]) @@ -130,6 +132,7 @@ def test_nmstate_config_bridge(self): eth1_iface = next(i for i in result["interfaces"] if i["name"] == "eth1") self.assertEqual(eth1_iface["type"], "ethernet") + self.assertEqual(eth1_iface["mtu"], 9000) def test_nmstate_config_bond(self): result = nmstate.nmstate_config(self.context, ["net4"]) From 9a83477023aa1435ccbbef6767f60cfb480a455d Mon Sep 17 00:00:00 2001 From: Bartosz Bezak Date: Wed, 19 Aug 2026 13:58:25 +0200 Subject: [PATCH 4/4] Fix nmstate bridge VLAN MTU Do not pass an MTU for VLAN interfaces on bridges when it is the same as the parent bridge MTU. NetworkManager may otherwise set an incorrect MTU after reboot. This applies the workaround from commit 6e2a551f8d342c2603da294cfa12ecb001a56240 to the nmstate network engine. Closes-Bug: #2164520 Related-Bug: #2039947 Change-Id: I9fb4ca153297c20672573e59815313a38d36c297 Signed-off-by: Bartosz Bezak (cherry picked from commit 3c149c0784a2a445aeb4436a881b9ba3a401dd5b) --- kayobe/plugins/filter/nmstate.py | 16 ++++++++ .../tests/unit/plugins/filter/test_nmstate.py | 38 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/kayobe/plugins/filter/nmstate.py b/kayobe/plugins/filter/nmstate.py index 7fae395f1..6feb5a9f3 100644 --- a/kayobe/plugins/filter/nmstate.py +++ b/kayobe/plugins/filter/nmstate.py @@ -586,6 +586,22 @@ def get_iface(name): parent = re.sub( r'\.{}$'.format(vlan_id), '', iface_name) + # NOTE(bbezak): Do not pass MTU for VLAN interfaces on bridges when + # it is identical to the parent bridge, to work around a + # NetworkManager bug. + bridge_mtus = {} + for bridge in networks.net_select_bridges( + context, names, inventory_hostname): + bridge_interface = networks.net_interface( + context, bridge, inventory_hostname) + bridge_mtus[bridge_interface] = networks.net_mtu( + context, bridge, inventory_hostname) + + if parent in bridge_mtus: + parent_mtu = bridge_mtus[parent] + if mtu and mtu == parent_mtu: + del iface["mtu"] + iface["vlan"] = { "base-iface": parent, "id": int(vlan_id) diff --git a/kayobe/tests/unit/plugins/filter/test_nmstate.py b/kayobe/tests/unit/plugins/filter/test_nmstate.py index da1fef140..a5d4614f9 100644 --- a/kayobe/tests/unit/plugins/filter/test_nmstate.py +++ b/kayobe/tests/unit/plugins/filter/test_nmstate.py @@ -434,6 +434,44 @@ def test_vlan_interface_explicit_vlan_and_parent(self): self.assertEqual(vlan_iface["vlan"]["base-iface"], "eth0") self.assertEqual(vlan_iface["vlan"]["id"], 100) + def test_vlan_on_bridge_inherits_matching_mtu(self): + variables = { + "inventory_hostname": "test-host", + "ansible_facts": {"os_family": "RedHat"}, + "vlan_interface": "br0.6", + "vlan_vlan": 6, + "vlan_mtu": 9150, + "bridge_interface": "br0", + "bridge_bridge_ports": ["eth0"], + "bridge_mtu": 9150, + } + context = self._make_context(variables) + result = nmstate.nmstate_config(context, ["vlan", "bridge"]) + + vlan_iface = next( + i for i in result["interfaces"] + if i["name"] == "br0.6") + self.assertNotIn("mtu", vlan_iface) + + def test_vlan_on_bridge_keeps_different_mtu(self): + variables = { + "inventory_hostname": "test-host", + "ansible_facts": {"os_family": "RedHat"}, + "vlan_interface": "br0.6", + "vlan_vlan": 6, + "vlan_mtu": 9000, + "bridge_interface": "br0", + "bridge_bridge_ports": ["eth0"], + "bridge_mtu": 9150, + } + context = self._make_context(variables) + result = nmstate.nmstate_config(context, ["vlan", "bridge"]) + + vlan_iface = next( + i for i in result["interfaces"] + if i["name"] == "br0.6") + self.assertEqual(vlan_iface["mtu"], 9000) + def test_vlan_interface_invalid_name(self): """Test VLAN with invalid interface name is skipped gracefully.""" variables = {