From 021f2f80357a6cdb9ed922dbcf073c5a617e4c1a Mon Sep 17 00:00:00 2001 From: Bertrand Lallau Date: Mon, 19 Dec 2016 11:18:06 +0100 Subject: [PATCH] DHCP: "reserved_dhcp_port" not well managed during startup During DHCP agent startup, same reserved DHCP port can be configured by 2 different DHCP agent (HA case). In this special error case (race condition case) a DhcpPortInUse RemoteError exception is triggered (Bug: #1425402). This exception is actually not caught well, instead of trying with the next "reserved DHCP port" an exception was raised. Change-Id: I40cb8884b36c4a7c14f470d3a126f4a9a0a98d47 Closes-bug: #1651512 --- neutron/agent/linux/dhcp.py | 3 +- neutron/tests/unit/agent/linux/test_dhcp.py | 47 +++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index 69ac3e166bb..af23e0fe485 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -39,7 +39,6 @@ from neutron.agent.linux import ip_lib from neutron.agent.linux import iptables_manager from neutron.cmd.sanity import checks from neutron.common import constants as n_const -from neutron.common import exceptions as n_exc from neutron.common import utils as common_utils from neutron.extensions import extra_dhcp_opt as edo_ext from neutron.ipam import utils as ipam_utils @@ -1241,7 +1240,7 @@ class DeviceManager(object): port.id, {'port': {'network_id': network.id, 'device_id': device_id}}) except oslo_messaging.RemoteError as e: - if e.exc_type == n_exc.DhcpPortInUse: + if e.exc_type == 'DhcpPortInUse': LOG.info(_LI("Skipping DHCP port %s as it is " "already in use"), port.id) continue diff --git a/neutron/tests/unit/agent/linux/test_dhcp.py b/neutron/tests/unit/agent/linux/test_dhcp.py index fbb200262ca..743a7f312fc 100644 --- a/neutron/tests/unit/agent/linux/test_dhcp.py +++ b/neutron/tests/unit/agent/linux/test_dhcp.py @@ -19,7 +19,9 @@ import mock import netaddr from neutron_lib import constants from oslo_config import cfg +import oslo_messaging from oslo_utils import fileutils +import testtools from neutron.agent.common import config from neutron.agent.linux import dhcp @@ -2413,6 +2415,51 @@ class TestDeviceManager(TestConfBase): 'ns-XXX', ['192.168.0.6/24', 'fdca:3ba5:a17a:4ba3::2/64'], namespace='qdhcp-ns') + def test__setup_reserved_dhcp_port_with_fake_remote_error(self): + """Test scenario where a fake_network has two reserved ports, and + update_dhcp_port fails for the first of those with a RemoteError + different than DhcpPortInUse. + """ + # Setup with a reserved DHCP port. + fake_network = FakeDualNetworkReserved2() + fake_network.tenant_id = 'Tenant A' + reserved_port_2 = fake_network.ports[-1] + + mock_plugin = mock.Mock() + dh = dhcp.DeviceManager(cfg.CONF, mock_plugin) + messaging_error = oslo_messaging.RemoteError( + exc_type='FakeRemoteError') + mock_plugin.update_dhcp_port.side_effect = [messaging_error, + reserved_port_2] + + with testtools.ExpectedException(oslo_messaging.RemoteError): + dh.setup_dhcp_port(fake_network) + + def test__setup_reserved_dhcp_port_with_known_remote_error(self): + """Test scenario where a fake_network has two reserved ports, and + update_dhcp_port fails for the first of those with a DhcpPortInUse + RemoteError. + """ + # Setup with a reserved DHCP port. + fake_network = FakeDualNetworkReserved2() + fake_network.tenant_id = 'Tenant A' + reserved_port_1 = fake_network.ports[-2] + reserved_port_2 = fake_network.ports[-1] + + mock_plugin = mock.Mock() + dh = dhcp.DeviceManager(cfg.CONF, mock_plugin) + messaging_error = oslo_messaging.RemoteError(exc_type='DhcpPortInUse') + mock_plugin.update_dhcp_port.side_effect = [messaging_error, + reserved_port_2] + + with mock.patch.object(dhcp.LOG, 'info') as log: + dh.setup_dhcp_port(fake_network) + self.assertEqual(1, log.call_count) + expected_calls = [mock.call(reserved_port_1.id, mock.ANY), + mock.call(reserved_port_2.id, mock.ANY)] + self.assertEqual(expected_calls, + mock_plugin.update_dhcp_port.call_args_list) + class TestDictModel(base.BaseTestCase):