diff --git a/quantum/agent/dhcp_agent.py b/quantum/agent/dhcp_agent.py index 5d8de731d..41ce571f2 100644 --- a/quantum/agent/dhcp_agent.py +++ b/quantum/agent/dhcp_agent.py @@ -80,7 +80,7 @@ class DhcpAgent(manager.Manager): self.device_manager = DeviceManager(self.conf, self.plugin_rpc) self.lease_relay = DhcpLeaseRelay(self.update_lease) - self.dhcp_driver_cls.check_version() + self.dhcp_version = self.dhcp_driver_cls.check_version() self._populate_networks_cache() def _populate_networks_cache(self): @@ -126,7 +126,8 @@ class DhcpAgent(manager.Manager): network, self.root_helper, self.device_manager, - self._ns_name(network)) + self._ns_name(network), + self.dhcp_version) getattr(driver, action)() return True diff --git a/quantum/agent/linux/dhcp.py b/quantum/agent/linux/dhcp.py index ff5e843a9..2844b1a62 100644 --- a/quantum/agent/linux/dhcp.py +++ b/quantum/agent/linux/dhcp.py @@ -66,12 +66,13 @@ class DhcpBase(object): __metaclass__ = abc.ABCMeta def __init__(self, conf, network, root_helper='sudo', - device_delegate=None, namespace=None): + device_delegate=None, namespace=None, version=None): self.conf = conf self.network = network self.root_helper = root_helper self.device_delegate = device_delegate self.namespace = namespace + self.version = version @abc.abstractmethod def enable(self): @@ -230,7 +231,7 @@ class Dnsmasq(DhcpLocalProcess): @classmethod def check_version(cls): - is_valid_version = None + ver = 0 try: cmd = ['dnsmasq', '--version'] out = utils.execute(cmd) @@ -245,7 +246,7 @@ class Dnsmasq(DhcpLocalProcess): LOG.warning(_('Unable to determine dnsmasq version. ' 'Please ensure that its version is %s ' 'or above!'), cls.MINIMUM_VERSION) - return is_valid_version + return float(ver) @classmethod def existing_dhcp_networks(cls, conf, root_helper): @@ -299,8 +300,12 @@ class Dnsmasq(DhcpLocalProcess): # TODO (mark): how do we indicate other options # ra-only, slaac, ra-nameservers, and ra-stateless. mode = 'static' - cmd.append('--dhcp-range=set:%s,%s,%s,%ss' % - (self._TAG_PREFIX % i, + if self.version >= self.MINIMUM_VERSION: + set_tag = 'set:' + else: + set_tag = '' + cmd.append('--dhcp-range=%s%s,%s,%s,%ss' % + (set_tag, self._TAG_PREFIX % i, netaddr.IPNetwork(subnet.cidr).network, mode, self.conf.dhcp_lease_time)) @@ -434,7 +439,11 @@ class Dnsmasq(DhcpLocalProcess): 'quantum-dhcp-agent-dnsmasq-lease-update') def _format_option(self, index, option_name, *args): - return ','.join(('tag:' + self._TAG_PREFIX % index, + if self.version >= self.MINIMUM_VERSION: + set_tag = 'tag:' + else: + set_tag = '' + return ','.join((set_tag + self._TAG_PREFIX % index, 'option:%s' % option_name) + args) @classmethod diff --git a/quantum/tests/unit/test_dhcp_agent.py b/quantum/tests/unit/test_dhcp_agent.py index 519dc2314..25e0cc193 100644 --- a/quantum/tests/unit/test_dhcp_agent.py +++ b/quantum/tests/unit/test_dhcp_agent.py @@ -218,7 +218,8 @@ class TestDhcpAgent(base.BaseTestCase): mock.ANY, 'sudo', mock.ANY, - 'qdhcp-1') + 'qdhcp-1', + mock.ANY) def test_call_driver_failure(self): network = mock.Mock() @@ -233,7 +234,8 @@ class TestDhcpAgent(base.BaseTestCase): mock.ANY, 'sudo', mock.ANY, - 'qdhcp-1') + 'qdhcp-1', + mock.ANY) self.assertEqual(log.call_count, 1) self.assertTrue(dhcp.needs_resync) diff --git a/quantum/tests/unit/test_linux_dhcp.py b/quantum/tests/unit/test_linux_dhcp.py index 7f317b7a4..f29a364bf 100644 --- a/quantum/tests/unit/test_linux_dhcp.py +++ b/quantum/tests/unit/test_linux_dhcp.py @@ -450,7 +450,8 @@ class TestDnsmasq(TestBase): argv.__getitem__.side_effect = fake_argv dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), device_delegate=delegate, - namespace='qdhcp-ns') + namespace='qdhcp-ns', + version=float(2.59)) dm.spawn_process() self.assertTrue(mocks['_output_opts_file'].called) self.execute.assert_called_once_with(expected, @@ -488,7 +489,8 @@ tag:tag1,option:classless-static-route,%s,%s""".lstrip() % (fake_v6, with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: conf_fn.return_value = '/foo/opts' - dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork()) + dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), + version=float(2.59)) dm._output_opts_file() self.safe.assert_called_once_with('/foo/opts', expected) @@ -500,7 +502,21 @@ tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1 tag:tag0,option:router,192.168.0.1""".lstrip() with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: conf_fn.return_value = '/foo/opts' - dm = dhcp.Dnsmasq(self.conf, FakeDualNetworkSingleDHCP()) + dm = dhcp.Dnsmasq(self.conf, FakeDualNetworkSingleDHCP(), + version=float(2.59)) + dm._output_opts_file() + + self.safe.assert_called_once_with('/foo/opts', expected) + + def test_output_opts_file_single_dhcp_ver2_48(self): + expected = """ +tag0,option:dns-server,8.8.8.8 +tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1 +tag0,option:router,192.168.0.1""".lstrip() + with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: + conf_fn.return_value = '/foo/opts' + dm = dhcp.Dnsmasq(self.conf, FakeDualNetworkSingleDHCP(), + version=float(2.48)) dm._output_opts_file() self.safe.assert_called_once_with('/foo/opts', expected) @@ -512,7 +528,8 @@ tag:tag0,option:router""".lstrip() with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: conf_fn.return_value = '/foo/opts' - dm = dhcp.Dnsmasq(self.conf, FakeV4NoGatewayNetwork()) + dm = dhcp.Dnsmasq(self.conf, FakeV4NoGatewayNetwork(), + version=float(2.59)) with mock.patch.object(dm, '_make_subnet_interface_ip_map') as ipm: ipm.return_value = {FakeV4SubnetNoGateway.id: '192.168.1.1'} @@ -551,7 +568,8 @@ tag:tag1,option:classless-static-route,%s,%s""".lstrip() % (fake_v6, with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid: pid.__get__ = mock.Mock(return_value=5) dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), - namespace='qdhcp-ns') + namespace='qdhcp-ns', + version=float(2.59)) method_name = '_make_subnet_interface_ip_map' with mock.patch.object(dhcp.Dnsmasq, @@ -593,7 +611,7 @@ tag:tag1,option:classless-static-route,%s,%s""".lstrip() % (fake_v6, with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid: pid.__get__ = mock.Mock(return_value=5) dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), - namespace='qdhcp-ns') + namespace='qdhcp-ns', version=float(2.59)) method_name = '_make_subnet_interface_ip_map' with mock.patch.object(dhcp.Dnsmasq, method_name) as ip_map: @@ -727,13 +745,16 @@ tag:tag1,option:classless-static-route,%s,%s""".lstrip() % (fake_v6, self.assertEqual(result, expected_value) def test_check_minimum_version(self): - self._check_version('Dnsmasq version 2.59 Copyright (c)...', True) + self._check_version('Dnsmasq version 2.59 Copyright (c)...', + float(2.59)) def test_check_future_version(self): - self._check_version('Dnsmasq version 2.65 Copyright (c)...', True) + self._check_version('Dnsmasq version 2.65 Copyright (c)...', + float(2.65)) def test_check_fail_version(self): - self._check_version('Dnsmasq version 2.48 Copyright (c)...', False) + self._check_version('Dnsmasq version 2.48 Copyright (c)...', + float(2.48)) def test_check_version_failed_cmd_execution(self): - self._check_version('Error while executing command', None) + self._check_version('Error while executing command', 0)