Merge "Dynamically adjust max number of leases"

This commit is contained in:
Jenkins 2013-09-18 14:37:49 +00:00 committed by Gerrit Code Review
commit bff364fdbf
3 changed files with 43 additions and 15 deletions

View File

@ -62,6 +62,9 @@
# Use another DNS server before any in /etc/resolv.conf. # Use another DNS server before any in /etc/resolv.conf.
# dnsmasq_dns_server = # dnsmasq_dns_server =
# Limit number of leases to prevent a denial-of-service.
# dnsmasq_lease_max = 16777216
# Location to DHCP lease relay UNIX domain socket # Location to DHCP lease relay UNIX domain socket
# dhcp_lease_relay_socket = $state_path/dhcp/lease_relay # dhcp_lease_relay_socket = $state_path/dhcp/lease_relay

View File

@ -50,6 +50,10 @@ OPTS = [
cfg.StrOpt('dnsmasq_dns_server', cfg.StrOpt('dnsmasq_dns_server',
help=_('Use another DNS server before any in ' help=_('Use another DNS server before any in '
'/etc/resolv.conf.')), '/etc/resolv.conf.')),
cfg.IntOpt(
'dnsmasq_lease_max',
default=(2 ** 24),
help=_('Limit number of leases to prevent a denial-of-service.')),
cfg.StrOpt('interface_driver', cfg.StrOpt('interface_driver',
help=_("The driver used to manage the virtual interface.")), help=_("The driver used to manage the virtual interface.")),
] ]
@ -309,13 +313,12 @@ class Dnsmasq(DhcpLocalProcess):
'--except-interface=lo', '--except-interface=lo',
'--pid-file=%s' % self.get_conf_file_name( '--pid-file=%s' % self.get_conf_file_name(
'pid', ensure_conf_dir=True), 'pid', ensure_conf_dir=True),
#TODO (mark): calculate value from cidr (defaults to 150)
#'--dhcp-lease-max=%s' % ?,
'--dhcp-hostsfile=%s' % self._output_hosts_file(), '--dhcp-hostsfile=%s' % self._output_hosts_file(),
'--dhcp-optsfile=%s' % self._output_opts_file(), '--dhcp-optsfile=%s' % self._output_opts_file(),
'--leasefile-ro', '--leasefile-ro',
] ]
possible_leases = 0
for i, subnet in enumerate(self.network.subnets): for i, subnet in enumerate(self.network.subnets):
# if a subnet is specified to have dhcp disabled # if a subnet is specified to have dhcp disabled
if not subnet.enable_dhcp: if not subnet.enable_dhcp:
@ -330,11 +333,20 @@ class Dnsmasq(DhcpLocalProcess):
set_tag = 'set:' set_tag = 'set:'
else: else:
set_tag = '' set_tag = ''
cidr = netaddr.IPNetwork(subnet.cidr)
cmd.append('--dhcp-range=%s%s,%s,%s,%ss' % cmd.append('--dhcp-range=%s%s,%s,%s,%ss' %
(set_tag, self._TAG_PREFIX % i, (set_tag, self._TAG_PREFIX % i,
netaddr.IPNetwork(subnet.cidr).network, cidr.network,
mode, mode,
self.conf.dhcp_lease_duration)) self.conf.dhcp_lease_duration))
possible_leases += cidr.size
# Cap the limit because creating lots of subnets can inflate
# this possible lease cap.
cmd.append('--dhcp-lease-max=%d' %
min(possible_leases, self.conf.dnsmasq_lease_max))
cmd.append('--conf-file=%s' % self.conf.dnsmasq_config_file) cmd.append('--conf-file=%s' % self.conf.dnsmasq_config_file)
if self.conf.dnsmasq_dns_server: if self.conf.dnsmasq_dns_server:

View File

@ -142,12 +142,14 @@ class FakeV4Network:
id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
subnets = [FakeV4Subnet()] subnets = [FakeV4Subnet()]
ports = [FakePort1()] ports = [FakePort1()]
namespace = 'qdhcp-ns'
class FakeV6Network: class FakeV6Network:
id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'
subnets = [FakeV6Subnet()] subnets = [FakeV6Subnet()]
ports = [FakePort2()] ports = [FakePort2()]
namespace = 'qdhcp-ns'
class FakeDualNetwork: class FakeDualNetwork:
@ -534,9 +536,10 @@ class TestDhcpLocalProcess(TestBase):
class TestDnsmasq(TestBase): class TestDnsmasq(TestBase):
def _test_spawn(self, extra_options): def _test_spawn(self, extra_options, network=FakeDualNetwork(),
max_leases=16777216):
def mock_get_conf_file_name(kind, ensure_conf_dir=False): def mock_get_conf_file_name(kind, ensure_conf_dir=False):
return '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/%s' % kind return '/dhcp/%s/%s' % (network.id, kind)
def fake_argv(index): def fake_argv(index):
if index == 0: if index == 0:
@ -550,7 +553,7 @@ class TestDnsmasq(TestBase):
'exec', 'exec',
'qdhcp-ns', 'qdhcp-ns',
'env', 'env',
'NEUTRON_NETWORK_ID=cccccccc-cccc-cccc-cccc-cccccccccccc', 'NEUTRON_NETWORK_ID=%s' % network.id,
'dnsmasq', 'dnsmasq',
'--no-hosts', '--no-hosts',
'--no-resolv', '--no-resolv',
@ -558,12 +561,17 @@ class TestDnsmasq(TestBase):
'--bind-interfaces', '--bind-interfaces',
'--interface=tap0', '--interface=tap0',
'--except-interface=lo', '--except-interface=lo',
'--pid-file=/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/pid', '--pid-file=/dhcp/%s/pid' % network.id,
'--dhcp-hostsfile=/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host', '--dhcp-hostsfile=/dhcp/%s/host' % network.id,
'--dhcp-optsfile=/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts', '--dhcp-optsfile=/dhcp/%s/opts' % network.id,
'--leasefile-ro', '--leasefile-ro']
'--dhcp-range=set:tag0,192.168.0.0,static,86400s',
'--dhcp-range=set:tag1,fdca:3ba5:a17a:4ba3::,static,86400s'] expected.extend(
'--dhcp-range=set:tag%d,%s,static,86400s' %
(i, s.cidr.split('/')[0])
for i, s in enumerate(network.subnets)
)
expected.append('--dhcp-lease-max=%d' % max_leases)
expected.extend(extra_options) expected.extend(extra_options)
self.execute.return_value = ('', '') self.execute.return_value = ('', '')
@ -576,14 +584,13 @@ class TestDnsmasq(TestBase):
with mock.patch.multiple(dhcp.Dnsmasq, **attrs_to_mock) as mocks: with mock.patch.multiple(dhcp.Dnsmasq, **attrs_to_mock) as mocks:
mocks['get_conf_file_name'].side_effect = mock_get_conf_file_name mocks['get_conf_file_name'].side_effect = mock_get_conf_file_name
mocks['_output_opts_file'].return_value = ( mocks['_output_opts_file'].return_value = (
'/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts' '/dhcp/%s/opts' % network.id
) )
mocks['interface_name'].__get__ = mock.Mock(return_value='tap0') mocks['interface_name'].__get__ = mock.Mock(return_value='tap0')
with mock.patch.object(dhcp.sys, 'argv') as argv: with mock.patch.object(dhcp.sys, 'argv') as argv:
argv.__getitem__.side_effect = fake_argv argv.__getitem__.side_effect = fake_argv
dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), dm = dhcp.Dnsmasq(self.conf, network, version=float(2.59))
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,
@ -607,6 +614,12 @@ class TestDnsmasq(TestBase):
'--server=8.8.8.8', '--server=8.8.8.8',
'--domain=openstacklocal']) '--domain=openstacklocal'])
def test_spawn_max_leases_is_smaller_than_cap(self):
self._test_spawn(
['--conf-file=', '--domain=openstacklocal'],
network=FakeV4Network(),
max_leases=256)
def test_output_opts_file(self): def test_output_opts_file(self):
fake_v6 = 'gdca:3ba5:a17a:4ba3::1' fake_v6 = 'gdca:3ba5:a17a:4ba3::1'
fake_v6_cidr = 'gdca:3ba5:a17a:4ba3::/64' fake_v6_cidr = 'gdca:3ba5:a17a:4ba3::/64'