Retry dhcp_release on failures
Sometimes calls to dhcp_release(6) do not result in removal of a lease from the leases file, for example, when the release packet is not received by dnsmasq. Trying more than once is recommended in this case. Instead of blindly trying some number of times, we monitor the lease file contents, and retry the dhcp_release(6) call when an entry still remains. This is possible since dhcp_release(6) is being run from the DHCP server itself. We try three times and wait 0.3 seconds between tries. We also now check for any stale leases in the leases file that are unknown to neutron, also trying to remove them. Change-Id: Ic1864f7efbc94db1369ac7f3e2879fda86f95a11 Closes-bug: #1764481
This commit is contained in:
parent
2f3fb39fa2
commit
fab032b426
@ -58,6 +58,8 @@ METADATA_PORT = 80
|
||||
WIN2k3_STATIC_DNS = 249
|
||||
NS_PREFIX = 'qdhcp-'
|
||||
DNSMASQ_SERVICE_NAME = 'dnsmasq'
|
||||
DHCP_RELEASE_TRIES = 3
|
||||
DHCP_RELEASE_TRIES_SLEEP = 0.3
|
||||
|
||||
|
||||
class DictModel(dict):
|
||||
@ -470,10 +472,10 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
"will not call it again.")
|
||||
return self._IS_DHCP_RELEASE6_SUPPORTED
|
||||
|
||||
def _release_lease(self, mac_address, ip, client_id=None,
|
||||
def _release_lease(self, mac_address, ip, ip_version, client_id=None,
|
||||
server_id=None, iaid=None):
|
||||
"""Release a DHCP lease."""
|
||||
if netaddr.IPAddress(ip).version == constants.IP_VERSION_6:
|
||||
if ip_version == constants.IP_VERSION_6:
|
||||
if not self._is_dhcp_release6_supported():
|
||||
return
|
||||
cmd = ['dhcp_release6', '--iface', self.interface_name,
|
||||
@ -754,15 +756,12 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
LOG.debug('Error while reading hosts file %s', filename)
|
||||
return leases
|
||||
|
||||
def _read_v6_leases_file_leases(self, filename):
|
||||
def _read_leases_file_leases(self, filename, ip_version=None):
|
||||
"""
|
||||
reading information from leases file which is needed to pass to
|
||||
Read information from leases file, which is needed to pass to
|
||||
dhcp_release6 command line utility if some of these leases are not
|
||||
needed anymore
|
||||
|
||||
in this method ipv4 entries in leases file are ignored, as info in
|
||||
hosts file is enough
|
||||
|
||||
each line in dnsmasq leases file is one of the following
|
||||
* duid entry: duid server_duid
|
||||
There MUST be single duid entry per file
|
||||
@ -791,7 +790,8 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
dnsmasq-discuss/2016q2/010595.html
|
||||
|
||||
:param filename: leases file
|
||||
:return: dict, keys are IPv6 addresses, values are dicts containing
|
||||
:param ip_version: IP version of entries to return, or None for all
|
||||
:return: dict, keys are IP(v6) addresses, values are dicts containing
|
||||
iaid, client_id and server_id
|
||||
"""
|
||||
leases = {}
|
||||
@ -812,7 +812,8 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
parts = l.strip().split()
|
||||
(iaid, ip, client_id) = parts[1], parts[2], parts[4]
|
||||
ip = ip.strip('[]')
|
||||
if netaddr.IPAddress(ip).version == constants.IP_VERSION_4:
|
||||
if (ip_version and
|
||||
netaddr.IPAddress(ip).version != ip_version):
|
||||
continue
|
||||
leases[ip] = {'iaid': iaid,
|
||||
'client_id': client_id,
|
||||
@ -824,26 +825,68 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
filename = self.get_conf_file_name('host')
|
||||
old_leases = self._read_hosts_file_leases(filename)
|
||||
leases_filename = self.get_conf_file_name('leases')
|
||||
# here is dhcpv6 stuff needed to craft dhcpv6 packet
|
||||
v6_leases = self._read_v6_leases_file_leases(leases_filename)
|
||||
cur_leases = self._read_leases_file_leases(leases_filename)
|
||||
if not cur_leases:
|
||||
return
|
||||
|
||||
v4_leases = set()
|
||||
for (k, v) in cur_leases.items():
|
||||
# IPv4 leases have a MAC, IPv6 ones do not, so we must ignore
|
||||
if netaddr.IPAddress(k).version == constants.IP_VERSION_4:
|
||||
# treat '*' as None, see note in _read_leases_file_leases()
|
||||
client_id = v['client_id']
|
||||
if client_id is '*':
|
||||
client_id = None
|
||||
v4_leases.add((k, v['iaid'], client_id))
|
||||
|
||||
new_leases = set()
|
||||
for port in self.network.ports:
|
||||
client_id = self._get_client_id(port)
|
||||
for alloc in port.fixed_ips:
|
||||
new_leases.add((alloc.ip_address, port.mac_address, client_id))
|
||||
|
||||
for ip, mac, client_id in old_leases - new_leases:
|
||||
entry = v6_leases.get(ip, None)
|
||||
version = netaddr.IPAddress(ip).version
|
||||
if entry:
|
||||
# must release IPv6 lease
|
||||
self._release_lease(mac, ip, entry['client_id'],
|
||||
entry['server_id'], entry['iaid'])
|
||||
# must release only if v4 lease. If we have ipv6 address missing
|
||||
# in old_leases, that means it's released already and nothing to do
|
||||
# here
|
||||
elif version == constants.IP_VERSION_4:
|
||||
self._release_lease(mac, ip, client_id)
|
||||
# If an entry is in the leases or host file(s), but doesn't have
|
||||
# a fixed IP on a corresponding neutron port, consider it stale.
|
||||
entries_to_release = (v4_leases | old_leases) - new_leases
|
||||
if not entries_to_release:
|
||||
return
|
||||
|
||||
# Try DHCP_RELEASE_TRIES times to release a lease, re-reading the
|
||||
# file each time to see if it's still there. We loop +1 times to
|
||||
# check the lease file one last time before logging any remaining
|
||||
# entries.
|
||||
for i in range(DHCP_RELEASE_TRIES + 1):
|
||||
entries_not_present = set()
|
||||
for ip, mac, client_id in entries_to_release:
|
||||
try:
|
||||
entry = cur_leases[ip]
|
||||
except KeyError:
|
||||
entries_not_present.add((ip, mac, client_id))
|
||||
continue
|
||||
# if not the final loop, try and release
|
||||
if i < DHCP_RELEASE_TRIES:
|
||||
ip_version = netaddr.IPAddress(ip).version
|
||||
if ip_version == constants.IP_VERSION_6:
|
||||
client_id = entry['client_id']
|
||||
self._release_lease(mac, ip, ip_version, client_id,
|
||||
entry['server_id'], entry['iaid'])
|
||||
|
||||
# Remove elements that were not in the current leases file,
|
||||
# no need to look for them again, and see if we're done.
|
||||
entries_to_release -= entries_not_present
|
||||
if not entries_to_release:
|
||||
break
|
||||
|
||||
if i < DHCP_RELEASE_TRIES:
|
||||
time.sleep(DHCP_RELEASE_TRIES_SLEEP)
|
||||
cur_leases = self._read_leases_file_leases(leases_filename)
|
||||
if not cur_leases:
|
||||
break
|
||||
else:
|
||||
LOG.warning("Could not release DHCP leases for these IP "
|
||||
"addresses after %d tries: %s",
|
||||
DHCP_RELEASE_TRIES,
|
||||
', '.join(ip for ip, m, c in entries_to_release))
|
||||
|
||||
def _output_addn_hosts_file(self):
|
||||
"""Writes a dnsmasq compatible additional hosts file.
|
||||
|
@ -1915,18 +1915,26 @@ class TestDnsmasq(TestBase):
|
||||
mac1 = '00:00:80:aa:bb:cc'
|
||||
ip2 = '192.168.1.3'
|
||||
mac2 = '00:00:80:cc:bb:aa'
|
||||
ip3 = '0001:0002:0003:004:0005:0006:0007:0008'
|
||||
ip3 = '0001:0002:0003:0004:0005:0006:0007:0008'
|
||||
mac3 = '00:00:80:bb:aa:cc'
|
||||
|
||||
old_leases = {(ip1, mac1, None), (ip2, mac2, None), (ip3, mac3, None)}
|
||||
dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases)
|
||||
dnsmasq._read_v6_leases_file_leases = mock.Mock(
|
||||
return_value={
|
||||
'0001:0002:0003:004:0005:0006:0007:0008':
|
||||
{'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'}}
|
||||
)
|
||||
# Because the lease release code could fire multiple times, the
|
||||
# second read of the lease file must not have the entries that
|
||||
# would have been released.
|
||||
dnsmasq._read_leases_file_leases = mock.Mock(
|
||||
side_effect=[{ip1: {'iaid': mac1,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'},
|
||||
ip2: {'iaid': mac2,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'},
|
||||
ip3: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'}
|
||||
},
|
||||
{}])
|
||||
|
||||
dnsmasq._output_hosts_file = mock.Mock()
|
||||
dnsmasq._release_lease = mock.Mock()
|
||||
@ -1935,12 +1943,16 @@ class TestDnsmasq(TestBase):
|
||||
|
||||
dnsmasq._release_unused_leases()
|
||||
|
||||
dnsmasq._release_lease.assert_has_calls([mock.call(mac1, ip1, None),
|
||||
mock.call(mac2, ip2, None),
|
||||
dnsmasq._release_lease.assert_has_calls([mock.call(mac1, ip1,
|
||||
constants.IP_VERSION_4,
|
||||
None, 'server_id', mac1),
|
||||
mock.call(mac2, ip2,
|
||||
constants.IP_VERSION_4,
|
||||
None, 'server_id', mac2),
|
||||
mock.call(mac3, ip3,
|
||||
'client_id',
|
||||
'server_id',
|
||||
0xff),
|
||||
constants.IP_VERSION_6,
|
||||
'client_id', 'server_id',
|
||||
0xff),
|
||||
],
|
||||
any_order=True)
|
||||
|
||||
@ -1952,17 +1964,24 @@ class TestDnsmasq(TestBase):
|
||||
ip2 = '192.168.1.3'
|
||||
mac2 = '00:00:80:cc:bb:aa'
|
||||
|
||||
old_leases = set([(ip1, mac1, None), (ip2, mac2, None)])
|
||||
old_leases = set([(ip1, mac1, 'client_id'), (ip2, mac2, 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'}
|
||||
})
|
||||
# Because the lease release code could fire multiple times, the
|
||||
# second read of the lease file must not have the entries that
|
||||
# would have been released.
|
||||
dnsmasq._read_leases_file_leases = mock.Mock(
|
||||
side_effect=[{ip1: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'},
|
||||
ip2: {'iaid': mac2,
|
||||
'client_id': None,
|
||||
'server_id': 'server_id'}
|
||||
},
|
||||
{}])
|
||||
ipw = mock.patch(
|
||||
'neutron.agent.linux.ip_lib.IpNetnsCommand.execute').start()
|
||||
dnsmasq._release_unused_leases()
|
||||
# Verify that dhcp_release is called both for ipv4 and ipv6 addresses.
|
||||
# Verify that dhcp_release is called both for ipv4 and ipv6 addresses.
|
||||
self.assertEqual(2, ipw.call_count)
|
||||
ipw.assert_has_calls([mock.call(['dhcp_release6',
|
||||
'--iface', None, '--ip', ip1,
|
||||
@ -1981,7 +2000,7 @@ class TestDnsmasq(TestBase):
|
||||
|
||||
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(
|
||||
dnsmasq._read_leases_file_leases = mock.Mock(
|
||||
return_value={'fdca:3ba5:a17a::1': {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'}
|
||||
@ -2003,7 +2022,7 @@ class TestDnsmasq(TestBase):
|
||||
|
||||
old_leases = set([(ip1, mac1, None), (ip2, mac2, None)])
|
||||
dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases)
|
||||
dnsmasq._read_v6_leases_file_leases = mock.Mock(
|
||||
dnsmasq._read_leases_file_leases = mock.Mock(
|
||||
return_value={ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'}
|
||||
@ -2031,11 +2050,24 @@ class TestDnsmasq(TestBase):
|
||||
|
||||
old_leases = set([(ip1, mac1, client_id1), (ip2, mac2, client_id2)])
|
||||
dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases)
|
||||
dnsmasq._read_v6_leases_file_leases = mock.Mock(
|
||||
return_value={ip6: {'iaid': 0xff,
|
||||
# Because the lease release code could fire multiple times, the
|
||||
# second read of the lease file must not have the entries that
|
||||
# would have been released.
|
||||
dnsmasq._read_leases_file_leases = mock.Mock(
|
||||
side_effect=[{ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'},
|
||||
ip1: {'iaid': mac1,
|
||||
'client_id': client_id1,
|
||||
'server_id': 'server_id'},
|
||||
ip2: {'iaid': mac2,
|
||||
'client_id': client_id2,
|
||||
'server_id': 'server_id'}
|
||||
},
|
||||
{ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'}
|
||||
})
|
||||
}])
|
||||
dnsmasq._output_hosts_file = mock.Mock()
|
||||
dnsmasq._release_lease = mock.Mock()
|
||||
dnsmasq.network.ports = []
|
||||
@ -2043,8 +2075,10 @@ class TestDnsmasq(TestBase):
|
||||
dnsmasq._release_unused_leases()
|
||||
|
||||
dnsmasq._release_lease.assert_has_calls(
|
||||
[mock.call(mac1, ip1, client_id1),
|
||||
mock.call(mac2, ip2, client_id2)],
|
||||
[mock.call(mac1, ip1, constants.IP_VERSION_4, client_id1,
|
||||
'server_id', mac1),
|
||||
mock.call(mac2, ip2, constants.IP_VERSION_4, client_id2,
|
||||
'server_id', mac2)],
|
||||
any_order=True)
|
||||
|
||||
def test_release_unused_leases_one_lease(self):
|
||||
@ -2058,11 +2092,21 @@ class TestDnsmasq(TestBase):
|
||||
|
||||
old_leases = set([(ip1, mac1, None), (ip2, mac2, None)])
|
||||
dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases)
|
||||
dnsmasq._read_v6_leases_file_leases = mock.Mock(
|
||||
return_value={ip6: {'iaid': 0xff,
|
||||
# Because the lease release code could fire multiple times, the
|
||||
# second read of the lease file must not have the entries that
|
||||
# would have been released.
|
||||
dnsmasq._read_leases_file_leases = mock.Mock(
|
||||
side_effect=[{ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'},
|
||||
ip2: {'iaid': mac2,
|
||||
'client_id': None,
|
||||
'server_id': 'server_id'}
|
||||
},
|
||||
{ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'}
|
||||
})
|
||||
}])
|
||||
dnsmasq._output_hosts_file = mock.Mock()
|
||||
dnsmasq._release_lease = mock.Mock()
|
||||
dnsmasq.network.ports = [FakePort1()]
|
||||
@ -2070,7 +2114,7 @@ class TestDnsmasq(TestBase):
|
||||
dnsmasq._release_unused_leases()
|
||||
|
||||
dnsmasq._release_lease.assert_called_once_with(
|
||||
mac2, ip2, None)
|
||||
mac2, ip2, constants.IP_VERSION_4, None, 'server_id', mac2)
|
||||
|
||||
def test_release_unused_leases_one_lease_with_client_id(self):
|
||||
dnsmasq = self._get_dnsmasq(FakeDualNetwork())
|
||||
@ -2086,18 +2130,138 @@ class TestDnsmasq(TestBase):
|
||||
old_leases = set([(ip1, mac1, client_id1), (ip2, mac2, client_id2)])
|
||||
dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases)
|
||||
dnsmasq._output_hosts_file = mock.Mock()
|
||||
dnsmasq._read_v6_leases_file_leases = mock.Mock(
|
||||
return_value={ip6: {'iaid': 0xff,
|
||||
# Because the lease release code could fire multiple times, the
|
||||
# second read of the lease file must not have the entries that
|
||||
# would have been released.
|
||||
dnsmasq._read_leases_file_leases = mock.Mock(
|
||||
side_effect=[{ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'},
|
||||
ip1: {'iaid': mac1,
|
||||
'client_id': client_id1,
|
||||
'server_id': 'server_id'}
|
||||
},
|
||||
{ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'}
|
||||
})
|
||||
}])
|
||||
dnsmasq._release_lease = mock.Mock()
|
||||
dnsmasq.network.ports = [FakePort5()]
|
||||
|
||||
dnsmasq._release_unused_leases()
|
||||
|
||||
dnsmasq._release_lease.assert_called_once_with(
|
||||
mac1, ip1, client_id1)
|
||||
mac1, ip1, constants.IP_VERSION_4, client_id1, 'server_id', mac1)
|
||||
|
||||
def test_release_unused_leases_one_lease_from_leases_file(self):
|
||||
# leases file has a stale entry that is not in the host file
|
||||
dnsmasq = self._get_dnsmasq(FakeDualNetwork())
|
||||
|
||||
ip1 = '192.168.0.2'
|
||||
mac1 = '00:00:80:aa:bb:cc'
|
||||
ip2 = '192.168.0.3'
|
||||
mac2 = '00:00:80:cc:bb:aa'
|
||||
ip6 = '2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d'
|
||||
|
||||
old_leases = set([(ip1, mac1, None)])
|
||||
dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases)
|
||||
# Because the lease release code could fire multiple times, the
|
||||
# second read of the lease file must not have the entries that
|
||||
# would have been released.
|
||||
dnsmasq._read_leases_file_leases = mock.Mock(
|
||||
side_effect=[{ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'},
|
||||
ip2: {'iaid': mac2,
|
||||
'client_id': None,
|
||||
'server_id': 'server_id'}
|
||||
},
|
||||
{ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'}
|
||||
}])
|
||||
dnsmasq._output_hosts_file = mock.Mock()
|
||||
dnsmasq._release_lease = mock.Mock()
|
||||
dnsmasq.network.ports = [FakePort1()]
|
||||
|
||||
dnsmasq._release_unused_leases()
|
||||
|
||||
dnsmasq._release_lease.assert_called_once_with(
|
||||
mac2, ip2, constants.IP_VERSION_4, None, 'server_id', mac2)
|
||||
|
||||
@mock.patch.object(dhcp.LOG, 'warn')
|
||||
def _test_release_unused_leases_one_lease_mult_times(self, mock_log_warn,
|
||||
removed):
|
||||
# Simulate a dhcp_release failure where the lease remains in the
|
||||
# lease file despite multiple dhcp_release calls
|
||||
dnsmasq = self._get_dnsmasq(FakeDualNetwork())
|
||||
|
||||
ip1 = '192.168.0.2'
|
||||
mac1 = '00:00:80:aa:bb:cc'
|
||||
ip2 = '192.168.0.3'
|
||||
mac2 = '00:00:80:cc:bb:aa'
|
||||
ip6 = '2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d'
|
||||
|
||||
old_leases = set([(ip1, mac1, None), (ip2, mac2, None)])
|
||||
dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases)
|
||||
# Because the lease release code could fire multiple times, the
|
||||
# second and subsequent reads of the lease file must have the
|
||||
# entries that were not released.
|
||||
side_effect = [{ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'},
|
||||
ip2: {'iaid': mac2,
|
||||
'client_id': None,
|
||||
'server_id': 'server_id'}
|
||||
},
|
||||
{ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'},
|
||||
ip2: {'iaid': mac2,
|
||||
'client_id': None,
|
||||
'server_id': 'server_id'}
|
||||
},
|
||||
{ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'},
|
||||
ip2: {'iaid': mac2,
|
||||
'client_id': None,
|
||||
'server_id': 'server_id'}
|
||||
}]
|
||||
# entry did/didn't go away after final dhcp_release try
|
||||
if not removed:
|
||||
side_effect.append(
|
||||
{ip6: {'iaid': 0xff,
|
||||
'client_id': 'client_id',
|
||||
'server_id': 'server_id'},
|
||||
ip2: {'iaid': mac2,
|
||||
'client_id': None,
|
||||
'server_id': 'server_id'}
|
||||
})
|
||||
else:
|
||||
side_effect.append({})
|
||||
|
||||
dnsmasq._read_leases_file_leases = mock.Mock(side_effect=side_effect)
|
||||
dnsmasq._output_hosts_file = mock.Mock()
|
||||
dnsmasq._release_lease = mock.Mock()
|
||||
dnsmasq.network.ports = [FakePort1()]
|
||||
|
||||
dnsmasq._release_unused_leases()
|
||||
|
||||
self.assertEqual(dhcp.DHCP_RELEASE_TRIES,
|
||||
dnsmasq._release_lease.call_count)
|
||||
|
||||
self.assertEqual(dhcp.DHCP_RELEASE_TRIES + 1,
|
||||
dnsmasq._read_leases_file_leases.call_count)
|
||||
|
||||
if not removed:
|
||||
self.assertTrue(mock_log_warn.called)
|
||||
|
||||
def test_release_unused_leases_one_lease_mult_times_not_removed(self):
|
||||
self._test_release_unused_leases_one_lease_mult_times(False)
|
||||
|
||||
def test_release_unused_leases_one_lease_mult_times_removed(self):
|
||||
self._test_release_unused_leases_one_lease_mult_times(True)
|
||||
|
||||
def test_read_hosts_file_leases(self):
|
||||
filename = '/path/to/file'
|
||||
@ -2145,12 +2309,12 @@ class TestDnsmasq(TestBase):
|
||||
("fdca:3ba5:a17a::1", "00:00:80:aa:bb:cc",
|
||||
'client2')]), leases)
|
||||
|
||||
def test_read_v6_leases_file_leases(self):
|
||||
def _test_read_leases_file_leases(self, ip_version):
|
||||
filename = '/path/to/file'
|
||||
lines = [
|
||||
"1472673289 aa:bb:cc:00:00:01 192.168.1.2 host-192-168-1-2 *",
|
||||
"1472673289 aa:bb:cc:00:00:01 192.168.1.3 host-192-168-1-3 *",
|
||||
"1472673289 aa:bb:cc:00:00:01 192.168.1.4 host-192-168-1-4 *",
|
||||
"1472673289 aa:bb:cc:00:00:02 192.168.1.2 host-192-168-1-2 *",
|
||||
"1472673289 aa:bb:cc:00:00:03 192.168.1.3 host-192-168-1-3 *",
|
||||
"1472673289 aa:bb:cc:00:00:04 192.168.1.4 host-192-168-1-4 *",
|
||||
"duid 00:01:00:01:02:03:04:05:06:07:08:09:0a:0b",
|
||||
"1472597740 1044800001 [2001:DB8::a] host-2001-db8--a "
|
||||
"00:04:4a:d0:d2:34:19:2b:49:08:84:e8:34:bd:0c:dc:b9:3b",
|
||||
@ -2164,14 +2328,13 @@ class TestDnsmasq(TestBase):
|
||||
|
||||
dnsmasq = self._get_dnsmasq(FakeDualNetwork())
|
||||
with mock.patch('os.path.exists', return_value=True):
|
||||
leases = dnsmasq._read_v6_leases_file_leases(filename)
|
||||
leases = dnsmasq._read_leases_file_leases(filename, ip_version)
|
||||
server_id = '00:01:00:01:02:03:04:05:06:07:08:09:0a:0b'
|
||||
entry1 = {'iaid': '1044800001',
|
||||
'client_id': '00:04:4a:d0:d2:34:19:2b:49:08:84:'
|
||||
'e8:34:bd:0c:dc:b9:3b',
|
||||
'server_id': server_id
|
||||
}
|
||||
|
||||
entry2 = {'iaid': '1044800002',
|
||||
'client_id': '00:04:ce:96:53:3d:f2:c2:4c:4c:81:'
|
||||
'7d:db:c9:8d:d2:74:22:3b:0a',
|
||||
@ -2182,13 +2345,46 @@ class TestDnsmasq(TestBase):
|
||||
'7f:5c:33:31:37:5d:80:77:b4',
|
||||
'server_id': server_id
|
||||
}
|
||||
expected = {'2001:DB8::a': entry1,
|
||||
'2001:DB8::b': entry2,
|
||||
'2001:DB8::c': entry3
|
||||
}
|
||||
v6_expected = {'2001:DB8::a': entry1,
|
||||
'2001:DB8::b': entry2,
|
||||
'2001:DB8::c': entry3
|
||||
}
|
||||
|
||||
entry4 = {'iaid': 'aa:bb:cc:00:00:02',
|
||||
'client_id': '*',
|
||||
'server_id': None
|
||||
}
|
||||
entry5 = {'iaid': 'aa:bb:cc:00:00:03',
|
||||
'client_id': '*',
|
||||
'server_id': None
|
||||
}
|
||||
entry6 = {'iaid': 'aa:bb:cc:00:00:04',
|
||||
'client_id': '*',
|
||||
'server_id': None
|
||||
}
|
||||
v4_expected = {'192.168.1.2': entry4,
|
||||
'192.168.1.3': entry5,
|
||||
'192.168.1.4': entry6
|
||||
}
|
||||
|
||||
expected = {}
|
||||
if not ip_version or ip_version == constants.IP_VERSION_6:
|
||||
expected.update(v6_expected)
|
||||
|
||||
if not ip_version or ip_version == constants.IP_VERSION_4:
|
||||
expected.update(v4_expected)
|
||||
|
||||
self.assertEqual(expected, leases)
|
||||
mock_open.assert_called_once_with(filename)
|
||||
self.assertEqual(expected, leases)
|
||||
|
||||
def test_read_v6_leases_file_leases(self):
|
||||
self._test_read_leases_file_leases(constants.IP_VERSION_6)
|
||||
|
||||
def test_read_v4_leases_file_leases(self):
|
||||
self._test_read_leases_file_leases(constants.IP_VERSION_4)
|
||||
|
||||
def test_read_all_leases_file_leases(self):
|
||||
self._test_read_leases_file_leases(None)
|
||||
|
||||
def test_make_subnet_interface_ip_map(self):
|
||||
with mock.patch('neutron.agent.linux.ip_lib.IPDevice') as ip_dev:
|
||||
|
Loading…
Reference in New Issue
Block a user