Call enable DHCP only if there are subnets with enabled DHCP in network

In the configure_dhcp_for_network() method in the DHCP agent, we should
call "enable" from the DHCP driver, and put ports as dhcp_ready_ports
only if there are subnets with enabled DHCP service in that network.
Before this patch it was done for network if there were ANY subnets
there. That in some rare condition could result in calling enable()
method from the DHCP driver, and putting ports into dhcp_ready_ports
set even if there were only subnets with disabled dhcp there.

That could cause the problem with reporting provisioning block completed
by the DHCP entity and lead to unexpected transition of the port from
DOWN to UP state on the server side.

Related-Bug: 1953478
Change-Id: I8b2cc04a824cd7996b9ba486f8ef614c92b242d5
This commit is contained in:
Slawek Kaplonski 2022-01-17 17:02:07 +01:00
parent 0a89986932
commit 1562f9141b
2 changed files with 43 additions and 1 deletions

View File

@ -400,7 +400,8 @@ class DhcpAgent(manager.Manager):
if not network.admin_state_up:
return
if len(network.subnets) and self.call_driver('enable', network):
if (any(s for s in network.subnets if s.enable_dhcp) and
self.call_driver('enable', network)):
self.update_isolated_metadata_proxy(network)
self.cache.put(network)
# After enabling dhcp for network, mark all existing

View File

@ -137,6 +137,15 @@ fake_port2 = dhcp.DictModel(id='12345678-1234-aaaa-123456789000',
revision_number=77,
fixed_ips=[fake_fixed_ip2])
fake_port_subnet_2 = dhcp.DictModel(
id='12345678-1234-aaaa-1234567890ab',
device_id='dhcp-12345678-1234-aaaa-1234567890ab',
device_owner='',
allocation_pools=fake_subnet2_allocation_pools,
mac_address='aa:bb:cc:dd:ee:ff',
network_id=FAKE_NETWORK_UUID,
fixed_ips=[fake_fixed_ip_subnet2])
fake_ipv6_port = dhcp.DictModel(id='12345678-1234-aaaa-123456789000',
device_owner='',
mac_address='aa:bb:cc:dd:ee:99',
@ -166,6 +175,12 @@ fake_network = dhcp.NetModel(id=FAKE_NETWORK_UUID,
subnets=[fake_subnet1, fake_subnet2],
ports=[fake_port1])
fake_network_no_dhcp_subnets = dhcp.NetModel(id=FAKE_NETWORK_UUID,
project_id=FAKE_PROJECT_ID,
admin_state_up=True,
subnets=[fake_subnet2],
ports=[fake_port_subnet_2])
fake_network_ipv6 = dhcp.NetModel(id=FAKE_NETWORK_UUID,
project_id=FAKE_PROJECT_ID,
admin_state_up=True,
@ -563,6 +578,32 @@ class TestDhcpAgent(base.BaseTestCase):
set(range(sync_max, sync_max + port_count)))
self.assertEqual(all_ports, ports_ready)
def test_configure_dhcp_for_network(self):
dhcp = dhcp_agent.DhcpAgent(HOSTNAME)
with mock.patch.object(
dhcp, 'update_isolated_metadata_proxy') as ump, \
mock.patch.object(
dhcp, 'call_driver', return_value=True):
dhcp.configure_dhcp_for_network(fake_network)
ump.assert_called_once_with(fake_network)
self.assertIn(fake_network.id, dhcp.cache.get_network_ids())
self.assertIn(fake_port1.id, dhcp.dhcp_ready_ports)
def test_configure_dhcp_for_network_no_subnets_with_dhcp_enabled(self):
dhcp = dhcp_agent.DhcpAgent(HOSTNAME)
with mock.patch.object(
dhcp, 'update_isolated_metadata_proxy') as ump, \
mock.patch.object(
dhcp, 'call_driver', return_value=True) as call_driver_mock:
dhcp.configure_dhcp_for_network(fake_network_no_dhcp_subnets)
ump.assert_not_called()
call_driver_mock.assert_not_called()
self.assertNotIn(fake_network_no_dhcp_subnets.id,
dhcp.cache.get_network_ids())
self.assertNotIn(fake_port_subnet_2.id, dhcp.dhcp_ready_ports)
@mock.patch.object(linux_utils, 'delete_if_exists')
def test_dhcp_ready_ports_updates_after_enable_dhcp(self, *args):
with mock.patch('neutron.agent.linux.ip_lib.'