From 5453c92a2e777b9a41989cc21d6064ffe4711e8d Mon Sep 17 00:00:00 2001 From: Sahid Orentino Ferdjaoui <sahid.ferdjaoui@industrialdiscipline.com> Date: Mon, 15 Jan 2024 17:11:23 +0100 Subject: [PATCH] dhcp: ensure that cleaning DHCP process with one segment happens first Previously, the code used to clean up old DHCP processes for a network before creating new ones supporting multiple segments per network could potentially not be executed first. Since disabling applies to cleaning the namespace, this could have led to the network setup being destroyed after being done. This change moves the part that cleans up the old DHCP setup to ensure it is executed first. Closes-bug: #2049615 Signed-off-by: Sahid Orentino Ferdjaoui <sahid.ferdjaoui@industrialdiscipline.com> Change-Id: Iecdb2d81ee077c9b9057d0708c5c88e159970039 --- neutron/agent/dhcp/agent.py | 25 ++++++++---------- neutron/tests/unit/agent/dhcp/test_agent.py | 28 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/neutron/agent/dhcp/agent.py b/neutron/agent/dhcp/agent.py index d298d4f36f1..ee3c8e74263 100644 --- a/neutron/agent/dhcp/agent.py +++ b/neutron/agent/dhcp/agent.py @@ -221,21 +221,18 @@ class DhcpAgent(manager.Manager): sid_subnets[subnet.get('segment_id')].append(subnet) if sid_subnets: ret = [] + + # TODO(sahid): This whole block bellow should be removed in future, + # when we know that all environements have migrated to at least + # zed. This is expected to help for environements that already + # have deployed RPN. First, disable the dhcp agent for the + # network. Then the process will recreate it considering a dhcp + # agent per segmentation id. + if action in ['enable', 'disable']: + self._call_driver( + 'disable', network, segment=None, block=True) + for seg_id, subnets in sid_subnets.items(): - - # TODO(sahid): This whole block bellow should be removed in - # future, when we know that all environements have migrated to - # at least zed. This is expected to help for environements - # that already have deployed RPN. For any first segment - # associated to a subnet we want to disable its dhcp - # agent. Then the process will recreate it considering a dhcp - # agent per segmentation id. - segment = sid_segment.get(seg_id) - if segment and segment.segment_index == 0: - if action in ['enable', 'disable']: - self._call_driver( - 'disable', network, segment=None, block=True) - net_seg = copy.deepcopy(network) net_seg.subnets = subnets ret.append(self._call_driver( diff --git a/neutron/tests/unit/agent/dhcp/test_agent.py b/neutron/tests/unit/agent/dhcp/test_agent.py index d53cb5cbd8b..7ad51c764b8 100644 --- a/neutron/tests/unit/agent/dhcp/test_agent.py +++ b/neutron/tests/unit/agent/dhcp/test_agent.py @@ -373,6 +373,34 @@ class TestDhcpAgent(base.BaseTestCase): mock.ANY, None) + def test_call_driver_enable_with_segments(self): + seg0 = dhcp.DictModel(id='seg0', segment_index=0) + seg1 = dhcp.DictModel(id='seg1', segment_index=1) + + sub0 = dhcp.DictModel(id='sub0', segment_id=seg0.id) + sub1 = dhcp.DictModel(id='sub1', segment_id=seg1.id) + + network = dhcp.NetModel( + id=FAKE_NETWORK_UUID, + project_id=FAKE_PROJECT_ID, + admin_state_up=True, + subnets=[sub0, sub1], + # We don't know order of segments stored. + segments=[seg1, seg0]) + + agent = dhcp_agent.DhcpAgent(cfg.CONF) + with mock.patch.object(agent, + '_call_driver') as _call_driver: + self.assertTrue(agent.call_driver('enable', network)) + # The public function call_driver() is calling the private + # _call_driver(). + _call_driver.assert_has_calls([ + mock.call("disable", network, segment=None, block=True), + # It is not possible to assert on 'network' as there is a copy + # happening. The copy will be removed, see bug #2051729. + mock.call("enable", mock.ANY, segment=seg0), + mock.call("enable", mock.ANY, segment=seg1)]) + def test_call_driver_no_network(self): network = None dhcp = dhcp_agent.DhcpAgent(cfg.CONF)