From 479718d362c9077a57eeb9263e93b274ea10f677 Mon Sep 17 00:00:00 2001 From: Tobias Urdin Date: Fri, 28 Nov 2025 10:45:07 +0100 Subject: [PATCH] Cleanup stale DHCP ports not bound to host This loops through the network ports when checking for stale devices that should not be there anymore and if the tap port exist and the binding:host_id says it's bound to another host than what the DHCP agent is on the port is considered stale and is unplugged. This makes sure that Neutron has self heal itself during this error condition where a DHCP port that is not bound to itself is still there causes connectivity issues due to for example IP conflict with the other DHCP agent. Partial-Bug: #2130885 Change-Id: I1df5d7984201498493026dc06c3e7750b8780a5e Signed-off-by: Tobias Urdin --- neutron/agent/linux/dhcp.py | 17 +++++++++++++++++ .../tests/functional/agent/linux/test_dhcp.py | 16 ++++++++++++++-- ...ix-partial-bug-2130885-91a00b85b34507b7.yaml | 5 +++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/fix-partial-bug-2130885-91a00b85b34507b7.yaml diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index 1231580366d..d3105cb0931 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -26,6 +26,7 @@ import time import netaddr from neutron_lib.api.definitions import extra_dhcp_opt as edo_ext +from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib import exceptions from neutron_lib.utils import file as file_utils @@ -1776,6 +1777,22 @@ class DeviceManager: for port in network.ports} hw_ports = {d.name for d in ns_ip.get_devices()} + for port in network.ports: + dev_name = self.driver.get_device_name(port) + if dev_name not in hw_ports: + continue + + host_id = port.get(portbindings.HOST_ID) + if host_id and host_id != self.conf.host: + LOG.warning(f"Found stale port {port.id} for network " + f"{network.id} bound to {host_id} that is " + f"not us {self.conf.host}, deleting") + try: + self.unplug(dev_name, network) + except Exception: + LOG.exception("Exception during stale dhcp bound " + "device cleanup") + for dev_name in hw_ports - db_ports: if dev_name != skip_dev_name: LOG.warning("Found stale device %s, deleting", dev_name) diff --git a/neutron/tests/functional/agent/linux/test_dhcp.py b/neutron/tests/functional/agent/linux/test_dhcp.py index 460e2a7438e..d7255dd6fd0 100644 --- a/neutron/tests/functional/agent/linux/test_dhcp.py +++ b/neutron/tests/functional/agent/linux/test_dhcp.py @@ -54,11 +54,18 @@ class TestDhcp(functional_base.BaseSudoTestCase): 'fixed_ips': [tests_base.AttributeDict( {'subnet_id': 'subnet_foo_id4', 'ip_address': '10.0.0.4'})] }) + wrong_port = tests_base.AttributeDict({ + 'id': 'foo_id5', + 'binding:host_id': 'wrong', + 'mac_address': '10:22:33:44:55:71', + 'fixed_ips': [tests_base.AttributeDict( + {'subnet_id': 'subnet_foo_id4', 'ip_address': '10.0.0.7'})] + }) network = { 'id': 'foo_id', 'project_id': 'foo_project', 'namespace': 'qdhcp-foo_id', - 'ports': [dhcp_port4], + 'ports': [dhcp_port4, wrong_port], 'subnets': [tests_base.AttributeDict({'id': 'subnet_foo_id', 'enable_dhcp': True, 'ipv6_address_mode': None, @@ -91,11 +98,16 @@ class TestDhcp(functional_base.BaseSudoTestCase): "tapfoo_id4", "10:22:33:44:55:70", namespace="qdhcp-foo_id") + dev_mgr.driver.plug("foo_id", + "foo_id5", + "tapfoo_id5", + "10:22:33:44:55:71", + namespace="qdhcp-foo_id") ipw = ip_lib.IPWrapper(namespace="qdhcp-foo_id") devices = ipw.get_devices() self.addCleanup(ipw.netns.delete, 'qdhcp-foo_id') self.assertEqual(sorted(["tapfoo_id2", "tapfoo_id3", - "tapfoo_id4"]), + "tapfoo_id4", "tapfoo_id5"]), sorted(map(str, devices))) # setting up dhcp for the network dev_mgr.setup(tests_base.AttributeDict(network)) diff --git a/releasenotes/notes/fix-partial-bug-2130885-91a00b85b34507b7.yaml b/releasenotes/notes/fix-partial-bug-2130885-91a00b85b34507b7.yaml new file mode 100644 index 00000000000..082965bea26 --- /dev/null +++ b/releasenotes/notes/fix-partial-bug-2130885-91a00b85b34507b7.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Partially fixed `bug #2130885 `__ where + a DHCP port for neutron-dhcp-agent bound on the wrong host was not cleaned up correctly.