From d4f92ede9ed52cd0b9059d993e3cf1f42d5ff57e Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Tue, 6 Sep 2016 14:44:41 -0400 Subject: [PATCH] Fix dhcp_release6 error when not supported If the system does not have a version of dnsmasq that supports dhcp_release6, warn about but otherwise handle it gracefully, and also don't even try to execute it the next time. Closes-Bug: #1622002 Related-Bug: #1624079 Change-Id: I6bb9547f9d9a9fcfb2357521f3f5bd1dc1dd5136 --- neutron/agent/linux/dhcp.py | 13 +++++++++++++ neutron/tests/unit/agent/linux/test_dhcp.py | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index 7f914af4826..94221bc3bd8 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -35,6 +35,7 @@ from neutron.agent.common import utils as agent_common_utils from neutron.agent.linux import external_process 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 ipv6_utils @@ -292,6 +293,8 @@ class Dnsmasq(DhcpLocalProcess): _ID = 'id:' + _IS_DHCP_RELEASE6_SUPPORTED = None + @classmethod def check_version(cls): pass @@ -442,10 +445,20 @@ class Dnsmasq(DhcpLocalProcess): service_name=DNSMASQ_SERVICE_NAME, monitored_process=pm) + def _is_dhcp_release6_supported(self): + if self._IS_DHCP_RELEASE6_SUPPORTED is None: + self._IS_DHCP_RELEASE6_SUPPORTED = checks.dhcp_release6_supported() + if not self._IS_DHCP_RELEASE6_SUPPORTED: + LOG.warning(_LW("dhcp_release6 is not present on this system, " + "will not call it again.")) + return self._IS_DHCP_RELEASE6_SUPPORTED + def _release_lease(self, mac_address, ip, client_id=None, server_id=None, iaid=None): """Release a DHCP lease.""" if netaddr.IPAddress(ip).version == constants.IP_VERSION_6: + if not self._is_dhcp_release6_supported(): + return cmd = ['dhcp_release6', '--iface', self.interface_name, '--ip', ip, '--client-id', client_id, '--server-id', server_id, '--iaid', iaid] diff --git a/neutron/tests/unit/agent/linux/test_dhcp.py b/neutron/tests/unit/agent/linux/test_dhcp.py index 97bdba87c75..f6ca3b100c5 100644 --- a/neutron/tests/unit/agent/linux/test_dhcp.py +++ b/neutron/tests/unit/agent/linux/test_dhcp.py @@ -850,6 +850,7 @@ class TestBase(TestConfBase): self.replace_p = mock.patch('neutron.common.utils.replace_file') self.execute_p = mock.patch('neutron.agent.common.utils.execute') + mock.patch('neutron.agent.linux.utils.execute').start() self.safe = self.replace_p.start() self.execute = self.execute_p.start() @@ -1705,6 +1706,26 @@ class TestDnsmasq(TestBase): ipw.assert_has_calls([mock.call(['dhcp_release', None, ip2, mac2], run_as_root=True), ]) + def test_release_for_ipv6_lease_no_dhcp_release6(self): + dnsmasq = self._get_dnsmasq(FakeDualNetwork()) + + ip1 = 'fdca:3ba5:a17a::1' + mac1 = '00:00:80:aa:bb:cc' + + old_leases = set([(ip1, mac1, None)]) + dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases) + dnsmasq._read_v6_leases_file_leases = mock.Mock( + return_value={'fdca:3ba5:a17a::1': {'iaid': 0xff, + 'client_id': 'client_id', + 'server_id': 'server_id'} + }) + ipw = mock.patch( + 'neutron.agent.linux.ip_lib.IpNetnsCommand.execute').start() + dnsmasq._IS_DHCP_RELEASE6_SUPPORTED = False + dnsmasq._release_unused_leases() + # Verify that dhcp_release6 is not called when it is not present + ipw.assert_not_called() + def test_release_unused_leases_with_dhcp_port(self): dnsmasq = self._get_dnsmasq(FakeNetworkDhcpPort()) ip1 = '192.168.1.2'