Add support for dnsmasq version 2.48

Following the bug fix for bug 1170793 we are able to add
in support for dnsmasq versions that do not support tags.

2.48 is the version shipped in RHEL 6.4

Change-Id: Ia7d2b1c0adb477159ce146bcd4323d4b2795bff5
This commit is contained in:
Gary Kotton 2013-05-14 13:20:44 +00:00
parent df6109ae70
commit 6012b7e97c
4 changed files with 53 additions and 20 deletions

View File

@ -80,7 +80,7 @@ class DhcpAgent(manager.Manager):
self.device_manager = DeviceManager(self.conf, self.plugin_rpc) self.device_manager = DeviceManager(self.conf, self.plugin_rpc)
self.lease_relay = DhcpLeaseRelay(self.update_lease) 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() self._populate_networks_cache()
def _populate_networks_cache(self): def _populate_networks_cache(self):
@ -126,7 +126,8 @@ class DhcpAgent(manager.Manager):
network, network,
self.root_helper, self.root_helper,
self.device_manager, self.device_manager,
self._ns_name(network)) self._ns_name(network),
self.dhcp_version)
getattr(driver, action)() getattr(driver, action)()
return True return True

View File

@ -66,12 +66,13 @@ class DhcpBase(object):
__metaclass__ = abc.ABCMeta __metaclass__ = abc.ABCMeta
def __init__(self, conf, network, root_helper='sudo', def __init__(self, conf, network, root_helper='sudo',
device_delegate=None, namespace=None): device_delegate=None, namespace=None, version=None):
self.conf = conf self.conf = conf
self.network = network self.network = network
self.root_helper = root_helper self.root_helper = root_helper
self.device_delegate = device_delegate self.device_delegate = device_delegate
self.namespace = namespace self.namespace = namespace
self.version = version
@abc.abstractmethod @abc.abstractmethod
def enable(self): def enable(self):
@ -225,7 +226,7 @@ class Dnsmasq(DhcpLocalProcess):
@classmethod @classmethod
def check_version(cls): def check_version(cls):
is_valid_version = None ver = 0
try: try:
cmd = ['dnsmasq', '--version'] cmd = ['dnsmasq', '--version']
out = utils.execute(cmd) out = utils.execute(cmd)
@ -240,7 +241,7 @@ class Dnsmasq(DhcpLocalProcess):
LOG.warning(_('Unable to determine dnsmasq version. ' LOG.warning(_('Unable to determine dnsmasq version. '
'Please ensure that its version is %s ' 'Please ensure that its version is %s '
'or above!'), cls.MINIMUM_VERSION) 'or above!'), cls.MINIMUM_VERSION)
return is_valid_version return float(ver)
@classmethod @classmethod
def existing_dhcp_networks(cls, conf, root_helper): def existing_dhcp_networks(cls, conf, root_helper):
@ -294,8 +295,12 @@ class Dnsmasq(DhcpLocalProcess):
# TODO(mark): how do we indicate other options # TODO(mark): how do we indicate other options
# ra-only, slaac, ra-nameservers, and ra-stateless. # ra-only, slaac, ra-nameservers, and ra-stateless.
mode = 'static' mode = 'static'
cmd.append('--dhcp-range=set:%s,%s,%s,%ss' % if self.version >= self.MINIMUM_VERSION:
(self._TAG_PREFIX % i, 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, netaddr.IPNetwork(subnet.cidr).network,
mode, mode,
self.conf.dhcp_lease_time)) self.conf.dhcp_lease_time))
@ -423,7 +428,11 @@ class Dnsmasq(DhcpLocalProcess):
'quantum-dhcp-agent-dnsmasq-lease-update') 'quantum-dhcp-agent-dnsmasq-lease-update')
def _format_option(self, index, option_name, *args): 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) 'option:%s' % option_name) + args)
@classmethod @classmethod

View File

@ -216,7 +216,8 @@ class TestDhcpAgent(base.BaseTestCase):
mock.ANY, mock.ANY,
'sudo', 'sudo',
mock.ANY, mock.ANY,
'qdhcp-1') 'qdhcp-1',
mock.ANY)
def test_call_driver_failure(self): def test_call_driver_failure(self):
network = mock.Mock() network = mock.Mock()
@ -231,7 +232,8 @@ class TestDhcpAgent(base.BaseTestCase):
mock.ANY, mock.ANY,
'sudo', 'sudo',
mock.ANY, mock.ANY,
'qdhcp-1') 'qdhcp-1',
mock.ANY)
self.assertEqual(log.call_count, 1) self.assertEqual(log.call_count, 1)
self.assertTrue(dhcp.needs_resync) self.assertTrue(dhcp.needs_resync)

View File

@ -448,7 +448,8 @@ class TestDnsmasq(TestBase):
argv.__getitem__.side_effect = fake_argv argv.__getitem__.side_effect = fake_argv
dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(),
device_delegate=delegate, device_delegate=delegate,
namespace='qdhcp-ns') namespace='qdhcp-ns',
version=float(2.59))
dm.spawn_process() dm.spawn_process()
self.assertTrue(mocks['_output_opts_file'].called) self.assertTrue(mocks['_output_opts_file'].called)
self.execute.assert_called_once_with(expected, self.execute.assert_called_once_with(expected,
@ -486,7 +487,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: with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
conf_fn.return_value = '/foo/opts' 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() dm._output_opts_file()
self.safe.assert_called_once_with('/foo/opts', expected) self.safe.assert_called_once_with('/foo/opts', expected)
@ -498,7 +500,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() tag:tag0,option:router,192.168.0.1""".lstrip()
with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
conf_fn.return_value = '/foo/opts' 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() dm._output_opts_file()
self.safe.assert_called_once_with('/foo/opts', expected) self.safe.assert_called_once_with('/foo/opts', expected)
@ -510,7 +526,8 @@ tag:tag0,option:router""".lstrip()
with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
conf_fn.return_value = '/foo/opts' 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: with mock.patch.object(dm, '_make_subnet_interface_ip_map') as ipm:
ipm.return_value = {FakeV4SubnetNoGateway.id: '192.168.1.1'} ipm.return_value = {FakeV4SubnetNoGateway.id: '192.168.1.1'}
@ -549,7 +566,8 @@ tag:tag1,option:classless-static-route,%s,%s""".lstrip() % (fake_v6,
with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid: with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid:
pid.__get__ = mock.Mock(return_value=5) pid.__get__ = mock.Mock(return_value=5)
dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(),
namespace='qdhcp-ns') namespace='qdhcp-ns',
version=float(2.59))
method_name = '_make_subnet_interface_ip_map' method_name = '_make_subnet_interface_ip_map'
with mock.patch.object(dhcp.Dnsmasq, with mock.patch.object(dhcp.Dnsmasq,
@ -590,7 +608,7 @@ tag:tag1,option:classless-static-route,%s,%s""".lstrip() % (fake_v6,
with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid: with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid:
pid.__get__ = mock.Mock(return_value=5) pid.__get__ = mock.Mock(return_value=5)
dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(),
namespace='qdhcp-ns') namespace='qdhcp-ns', version=float(2.59))
method_name = '_make_subnet_interface_ip_map' method_name = '_make_subnet_interface_ip_map'
with mock.patch.object(dhcp.Dnsmasq, method_name) as ip_map: with mock.patch.object(dhcp.Dnsmasq, method_name) as ip_map:
@ -724,13 +742,16 @@ tag:tag1,option:classless-static-route,%s,%s""".lstrip() % (fake_v6,
self.assertEqual(result, expected_value) self.assertEqual(result, expected_value)
def test_check_minimum_version(self): 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): 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): 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): def test_check_version_failed_cmd_execution(self):
self._check_version('Error while executing command', None) self._check_version('Error while executing command', 0)