Merge "Support Stateful and Stateless DHCPv6 by dnsmasq"

This commit is contained in:
Jenkins 2014-08-23 17:24:24 +00:00 committed by Gerrit Code Review
commit 00d3af481e
2 changed files with 148 additions and 144 deletions

View File

@ -313,10 +313,11 @@ class Dnsmasq(DhcpLocalProcess):
ver = re.findall("\d+.\d+", out)[0] ver = re.findall("\d+.\d+", out)[0]
is_valid_version = float(ver) >= cls.MINIMUM_VERSION is_valid_version = float(ver) >= cls.MINIMUM_VERSION
if not is_valid_version: if not is_valid_version:
LOG.warning(_('FAILED VERSION REQUIREMENT FOR DNSMASQ. ' LOG.error(_('FAILED VERSION REQUIREMENT FOR DNSMASQ. '
'DHCP AGENT MAY NOT RUN CORRECTLY! ' 'DHCP AGENT MAY NOT RUN CORRECTLY! '
'Please ensure that its version is %s ' 'Please ensure that its version is %s '
'or above!'), cls.MINIMUM_VERSION) 'or above!'), cls.MINIMUM_VERSION)
raise SystemExit(1)
except (OSError, RuntimeError, IndexError, ValueError): except (OSError, RuntimeError, IndexError, ValueError):
LOG.error(_('Unable to determine dnsmasq version. ' LOG.error(_('Unable to determine dnsmasq version. '
'Please ensure that its version is %s ' 'Please ensure that its version is %s '
@ -368,17 +369,12 @@ class Dnsmasq(DhcpLocalProcess):
else: else:
# Note(scollins) If the IPv6 attributes are not set, set it as # Note(scollins) If the IPv6 attributes are not set, set it as
# static to preserve previous behavior # static to preserve previous behavior
if (not getattr(subnet, 'ipv6_ra_mode', None) and addr_mode = getattr(subnet, 'ipv6_address_mode', None)
not getattr(subnet, 'ipv6_address_mode', None)): ra_mode = getattr(subnet, 'ipv6_ra_mode', None)
if (addr_mode in [constants.DHCPV6_STATEFUL,
constants.DHCPV6_STATELESS] or
not addr_mode and not ra_mode):
mode = 'static' mode = 'static'
elif getattr(subnet, 'ipv6_ra_mode', None) is None:
# RA mode is not set - do not launch dnsmasq
continue
if self.version >= self.MINIMUM_VERSION:
set_tag = 'set:'
else:
set_tag = ''
cidr = netaddr.IPNetwork(subnet.cidr) cidr = netaddr.IPNetwork(subnet.cidr)
@ -390,14 +386,9 @@ class Dnsmasq(DhcpLocalProcess):
# mode is optional and is not set - skip it # mode is optional and is not set - skip it
if mode: if mode:
cmd.append('--dhcp-range=%s%s,%s,%s,%s' % cmd.append('--dhcp-range=%s%s,%s,%s,%s' %
(set_tag, self._TAG_PREFIX % i, ('set:', self._TAG_PREFIX % i,
cidr.network, mode, lease)) cidr.network, mode, lease))
else: possible_leases += cidr.size
cmd.append('--dhcp-range=%s%s,%s,%s' %
(set_tag, self._TAG_PREFIX % i,
cidr.network, lease))
possible_leases += cidr.size
# Cap the limit because creating lots of subnets can inflate # Cap the limit because creating lots of subnets can inflate
# this possible lease cap. # this possible lease cap.
@ -465,9 +456,8 @@ class Dnsmasq(DhcpLocalProcess):
# associated with the subnet being managed by this # associated with the subnet being managed by this
# dhcp agent # dhcp agent
if alloc.subnet_id in v6_nets: if alloc.subnet_id in v6_nets:
ra_mode = v6_nets[alloc.subnet_id].ipv6_ra_mode
addr_mode = v6_nets[alloc.subnet_id].ipv6_address_mode addr_mode = v6_nets[alloc.subnet_id].ipv6_address_mode
if (ra_mode is None and addr_mode == constants.IPV6_SLAAC): if addr_mode != constants.DHCPV6_STATEFUL:
continue continue
hostname = 'host-%s' % alloc.ip_address.replace( hostname = 'host-%s' % alloc.ip_address.replace(
'.', '-').replace(':', '-') '.', '-').replace(':', '-')
@ -497,7 +487,6 @@ class Dnsmasq(DhcpLocalProcess):
LOG.debug(_('Building host file: %s'), filename) LOG.debug(_('Building host file: %s'), filename)
for (port, alloc, hostname, name) in self._iter_hosts(): for (port, alloc, hostname, name) in self._iter_hosts():
set_tag = ''
# (dzyu) Check if it is legal ipv6 address, if so, need wrap # (dzyu) Check if it is legal ipv6 address, if so, need wrap
# it with '[]' to let dnsmasq to distinguish MAC address from # it with '[]' to let dnsmasq to distinguish MAC address from
# IPv6 address. # IPv6 address.
@ -510,12 +499,9 @@ class Dnsmasq(DhcpLocalProcess):
"ip": ip_address}) "ip": ip_address})
if getattr(port, 'extra_dhcp_opts', False): if getattr(port, 'extra_dhcp_opts', False):
if self.version >= self.MINIMUM_VERSION:
set_tag = 'set:'
buf.write('%s,%s,%s,%s%s\n' % buf.write('%s,%s,%s,%s%s\n' %
(port.mac_address, name, ip_address, (port.mac_address, name, ip_address,
set_tag, port.id)) 'set:', port.id))
else: else:
buf.write('%s,%s,%s\n' % buf.write('%s,%s,%s\n' %
(port.mac_address, name, ip_address)) (port.mac_address, name, ip_address))
@ -575,17 +561,27 @@ class Dnsmasq(DhcpLocalProcess):
dhcp_ips = collections.defaultdict(list) dhcp_ips = collections.defaultdict(list)
subnet_idx_map = {} subnet_idx_map = {}
for i, subnet in enumerate(self.network.subnets): for i, subnet in enumerate(self.network.subnets):
if not subnet.enable_dhcp: if (not subnet.enable_dhcp or
(subnet.ip_version == 6 and
getattr(subnet, 'ipv6_address_mode', None)
in [None, constants.IPV6_SLAAC])):
continue continue
if subnet.dns_nameservers: if subnet.dns_nameservers:
options.append( options.append(
self._format_option(i, 'dns-server', self._format_option(
','.join(subnet.dns_nameservers))) subnet.ip_version, i, 'dns-server',
','.join(
Dnsmasq._convert_to_literal_addrs(
subnet.ip_version, subnet.dns_nameservers))))
else: else:
# use the dnsmasq ip as nameservers only if there is no # use the dnsmasq ip as nameservers only if there is no
# dns-server submitted by the server # dns-server submitted by the server
subnet_idx_map[subnet.id] = i subnet_idx_map[subnet.id] = i
if self.conf.dhcp_domain and subnet.ip_version == 6:
options.append('tag:tag%s,option6:domain-search,%s' %
(i, ''.join(self.conf.dhcp_domain)))
gateway = subnet.gateway_ip gateway = subnet.gateway_ip
host_routes = [] host_routes = []
for hr in subnet.host_routes: for hr in subnet.host_routes:
@ -603,27 +599,42 @@ class Dnsmasq(DhcpLocalProcess):
'%s/32,%s' % (METADATA_DEFAULT_IP, subnet_dhcp_ip) '%s/32,%s' % (METADATA_DEFAULT_IP, subnet_dhcp_ip)
) )
if host_routes:
if gateway and subnet.ip_version == 4:
host_routes.append("%s,%s" % ("0.0.0.0/0", gateway))
options.append(
self._format_option(i, 'classless-static-route',
','.join(host_routes)))
options.append(
self._format_option(i, WIN2k3_STATIC_DNS,
','.join(host_routes)))
if subnet.ip_version == 4: if subnet.ip_version == 4:
if host_routes:
if gateway:
host_routes.append("%s,%s" % ("0.0.0.0/0", gateway))
options.append(
self._format_option(subnet.ip_version, i,
'classless-static-route',
','.join(host_routes)))
options.append(
self._format_option(subnet.ip_version, i,
WIN2k3_STATIC_DNS,
','.join(host_routes)))
if gateway: if gateway:
options.append(self._format_option(i, 'router', gateway)) options.append(self._format_option(subnet.ip_version,
i, 'router',
gateway))
else: else:
options.append(self._format_option(i, 'router')) options.append(self._format_option(subnet.ip_version,
i, 'router'))
for port in self.network.ports: for port in self.network.ports:
if getattr(port, 'extra_dhcp_opts', False): if getattr(port, 'extra_dhcp_opts', False):
options.extend( for ip_version in (4, 6):
self._format_option(port.id, opt.opt_name, opt.opt_value) if any(
for opt in port.extra_dhcp_opts) netaddr.IPAddress(ip.ip_address).version == ip_version
for ip in port.fixed_ips):
options.extend(
# TODO(xuhanp):Instead of applying extra_dhcp_opts
# to both DHCPv4 and DHCPv6, we need to find a new
# way to specify options for v4 and v6
# respectively. We also need to validate the option
# before applying it.
self._format_option(ip_version, port.id,
opt.opt_name, opt.opt_value)
for opt in port.extra_dhcp_opts)
# provides all dnsmasq ip as dns-server if there is more than # provides all dnsmasq ip as dns-server if there is more than
# one dnsmasq for a subnet and there is no dns-server submitted # one dnsmasq for a subnet and there is no dns-server submitted
@ -636,10 +647,16 @@ class Dnsmasq(DhcpLocalProcess):
dhcp_ips[i].append(ip.ip_address) dhcp_ips[i].append(ip.ip_address)
for i, ips in dhcp_ips.items(): for i, ips in dhcp_ips.items():
if len(ips) > 1: for ip_version in (4, 6):
options.append(self._format_option(i, vx_ips = [ip for ip in ips
'dns-server', if netaddr.IPAddress(ip).version == ip_version]
','.join(ips))) if vx_ips:
options.append(
self._format_option(
ip_version, i, 'dns-server',
','.join(
Dnsmasq._convert_to_literal_addrs(ip_version,
vx_ips))))
name = self.get_conf_file_name('opts') name = self.get_conf_file_name('opts')
utils.replace_file(name, '\n'.join(options)) utils.replace_file(name, '\n'.join(options))
@ -667,22 +684,26 @@ class Dnsmasq(DhcpLocalProcess):
return retval return retval
def _format_option(self, tag, option, *args): def _format_option(self, ip_version, tag, option, *args):
"""Format DHCP option by option name or code.""" """Format DHCP option by option name or code."""
if self.version >= self.MINIMUM_VERSION:
set_tag = 'tag:'
else:
set_tag = ''
option = str(option) option = str(option)
if isinstance(tag, int): if isinstance(tag, int):
tag = self._TAG_PREFIX % tag tag = self._TAG_PREFIX % tag
if not option.isdigit(): if not option.isdigit():
option = 'option:%s' % option if ip_version == 4:
option = 'option:%s' % option
else:
option = 'option6:%s' % option
return ','.join((set_tag + tag, '%s' % option) + args) return ','.join(('tag:' + tag, '%s' % option) + args)
@staticmethod
def _convert_to_literal_addrs(ip_version, ips):
if ip_version == 4:
return ips
return ['[' + ip + ']' for ip in ips]
def _enable_metadata(self, subnet): def _enable_metadata(self, subnet):
'''Determine if the metadata route will be pushed to hosts on subnet. '''Determine if the metadata route will be pushed to hosts on subnet.

View File

@ -17,6 +17,7 @@ import contextlib
import os import os
import mock import mock
import netaddr
from oslo.config import cfg from oslo.config import cfg
import testtools import testtools
@ -60,8 +61,8 @@ class FakePort2:
id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
admin_state_up = False admin_state_up = False
device_owner = 'foo2' device_owner = 'foo2'
fixed_ips = [FakeIPAllocation('fdca:3ba5:a17a:4ba3::2', fixed_ips = [FakeIPAllocation('192.168.0.3',
'ffffffff-ffff-ffff-ffff-ffffffffffff')] 'dddddddd-dddd-dddd-dddd-dddddddddddd')]
mac_address = '00:00:f3:aa:bb:cc' mac_address = '00:00:f3:aa:bb:cc'
def __init__(self): def __init__(self):
@ -72,10 +73,10 @@ class FakePort3:
id = '44444444-4444-4444-4444-444444444444' id = '44444444-4444-4444-4444-444444444444'
admin_state_up = True admin_state_up = True
device_owner = 'foo3' device_owner = 'foo3'
fixed_ips = [FakeIPAllocation('192.168.0.3', fixed_ips = [FakeIPAllocation('192.168.0.4',
'dddddddd-dddd-dddd-dddd-dddddddddddd'), 'dddddddd-dddd-dddd-dddd-dddddddddddd'),
FakeIPAllocation('fdca:3ba5:a17a:4ba3::3', FakeIPAllocation('192.168.1.2',
'ffffffff-ffff-ffff-ffff-ffffffffffff')] 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')]
mac_address = '00:00:0f:aa:bb:cc' mac_address = '00:00:0f:aa:bb:cc'
def __init__(self): def __init__(self):
@ -95,6 +96,32 @@ class FakePort4:
self.extra_dhcp_opts = [] self.extra_dhcp_opts = []
class FakeV6Port:
id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh'
admin_state_up = True
device_owner = 'foo3'
fixed_ips = [FakeIPAllocation('fdca:3ba5:a17a:4ba3::2',
'ffffffff-ffff-ffff-ffff-ffffffffffff')]
mac_address = '00:00:f3:aa:bb:cc'
def __init__(self):
self.extra_dhcp_opts = []
class FakeDualPort:
id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh'
admin_state_up = True
device_owner = 'foo3'
fixed_ips = [FakeIPAllocation('192.168.0.3',
'dddddddd-dddd-dddd-dddd-dddddddddddd'),
FakeIPAllocation('fdca:3ba5:a17a:4ba3::3',
'ffffffff-ffff-ffff-ffff-ffffffffffff')]
mac_address = '00:00:0f:aa:bb:cc'
def __init__(self):
self.extra_dhcp_opts = []
class FakeRouterPort: class FakeRouterPort:
id = 'rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr' id = 'rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr'
admin_state_up = True admin_state_up = True
@ -224,6 +251,18 @@ class FakeV4SubnetNoDHCP:
dns_nameservers = [] dns_nameservers = []
class FakeV6SubnetDHCPStateful:
id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
ip_version = 6
cidr = 'fdca:3ba5:a17a:4ba3::/64'
gateway_ip = 'fdca:3ba5:a17a:4ba3::1'
enable_dhcp = True
host_routes = [FakeV6HostRoute]
dns_nameservers = ['2001:0200:feed:7ac0::1']
ipv6_ra_mode = None
ipv6_address_mode = constants.DHCPV6_STATEFUL
class FakeV6SubnetSlaac: class FakeV6SubnetSlaac:
id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
ip_version = 6 ip_version = 6
@ -271,14 +310,14 @@ class FakeV6Network:
class FakeDualNetwork: class FakeDualNetwork:
id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
subnets = [FakeV4Subnet(), FakeV6Subnet()] subnets = [FakeV4Subnet(), FakeV6SubnetDHCPStateful()]
ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()] ports = [FakePort1(), FakeV6Port(), FakeDualPort(), FakeRouterPort()]
namespace = 'qdhcp-ns' namespace = 'qdhcp-ns'
class FakeDualNetworkGatewayRoute: class FakeDualNetworkGatewayRoute:
id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
subnets = [FakeV4SubnetGatewayRoute(), FakeV6Subnet()] subnets = [FakeV4SubnetGatewayRoute(), FakeV6SubnetDHCPStateful()]
ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()] ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()]
namespace = 'qdhcp-ns' namespace = 'qdhcp-ns'
@ -724,11 +763,16 @@ class TestDnsmasq(TestBase):
prefix = '--dhcp-range=set:tag%d,%s,static,%s%s' prefix = '--dhcp-range=set:tag%d,%s,static,%s%s'
else: else:
prefix = '--dhcp-range=set:tag%d,%s,%s%s' prefix = '--dhcp-range=set:tag%d,%s,%s%s'
expected.extend(prefix % possible_leases = 0
(i, s.cidr.split('/')[0], lease_duration, seconds) for i, s in enumerate(network.subnets):
for i, s in enumerate(network.subnets)) if (s.ip_version != 6
or s.ipv6_address_mode == constants.DHCPV6_STATEFUL):
expected.extend([prefix % (
i, s.cidr.split('/')[0], lease_duration, seconds)])
possible_leases += netaddr.IPNetwork(s.cidr).size
expected.append('--dhcp-lease-max=%d' % max_leases) expected.append('--dhcp-lease-max=%d' % min(
possible_leases, max_leases))
expected.extend(extra_options) expected.extend(extra_options)
self.execute.return_value = ('', '') self.execute.return_value = ('', '')
@ -775,10 +819,9 @@ class TestDnsmasq(TestBase):
self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data), self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data),
mock.call(exp_addn_name, exp_addn_data)]) mock.call(exp_addn_name, exp_addn_data)])
def test_spawn_no_dnsmasq_ipv6_mode(self): def test_spawn_no_dhcp_range(self):
network = FakeV6Network() network = FakeV6Network()
subnet = FakeV6Subnet() subnet = FakeV6SubnetSlaac()
subnet.ipv6_ra_mode = True
network.subnets = [subnet] network.subnets = [subnet]
self._test_spawn(['--conf-file=', '--domain=openstacklocal'], self._test_spawn(['--conf-file=', '--domain=openstacklocal'],
network, has_static=False) network, has_static=False)
@ -805,18 +848,15 @@ class TestDnsmasq(TestBase):
def test_output_opts_file(self): def test_output_opts_file(self):
fake_v6 = '2001:0200:feed:7ac0::1' fake_v6 = '2001:0200:feed:7ac0::1'
fake_v6_cidr = '2001:0200:feed:7ac0::/64'
expected = ( expected = (
'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:dns-server,8.8.8.8\n'
'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,'
'0.0.0.0/0,192.168.0.1\n' '0.0.0.0/0,192.168.0.1\n'
'tag:tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n'
'tag:tag0,option:router,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1\n'
'tag:tag1,option:dns-server,%s\n' 'tag:tag1,option6:dns-server,%s\n'
'tag:tag1,option:classless-static-route,%s,%s\n' 'tag:tag1,option6:domain-search,openstacklocal').lstrip() % (
'tag:tag1,249,%s,%s').lstrip() % (fake_v6, '[' + fake_v6 + ']')
fake_v6_cidr, fake_v6,
fake_v6_cidr, 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'
@ -828,15 +868,12 @@ class TestDnsmasq(TestBase):
def test_output_opts_file_gateway_route(self): def test_output_opts_file_gateway_route(self):
fake_v6 = '2001:0200:feed:7ac0::1' fake_v6 = '2001:0200:feed:7ac0::1'
fake_v6_cidr = '2001:0200:feed:7ac0::/64'
expected = """ expected = """
tag:tag0,option:dns-server,8.8.8.8 tag:tag0,option:dns-server,8.8.8.8
tag:tag0,option:router,192.168.0.1 tag:tag0,option:router,192.168.0.1
tag:tag1,option:dns-server,%s tag:tag1,option6:dns-server,%s
tag:tag1,option:classless-static-route,%s,%s tag:tag1,option6:domain-search,openstacklocal""".lstrip() % (
tag:tag1,249,%s,%s""".lstrip() % (fake_v6, '[' + fake_v6 + ']')
fake_v6_cidr, fake_v6,
fake_v6_cidr, 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'
@ -885,21 +922,6 @@ tag:tag0,option:router,192.168.0.1""".lstrip()
self.safe.assert_called_once_with('/foo/opts', expected) 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\n'
'tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,'
'0.0.0.0/0,192.168.0.1\n'
'tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n'
'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)
def test_output_opts_file_no_gateway(self): def test_output_opts_file_no_gateway(self):
expected = """ expected = """
tag:tag0,option:classless-static-route,169.254.169.254/32,192.168.1.1 tag:tag0,option:classless-static-route,169.254.169.254/32,192.168.1.1
@ -997,42 +1019,6 @@ tag:tag0,option:router""".lstrip()
self.safe.assert_called_once_with('/foo/opts', expected) self.safe.assert_called_once_with('/foo/opts', expected)
def test_output_opts_file_pxe_3port_1net_diff_details(self):
expected = (
'tag:tag0,option:dns-server,8.8.8.8\n'
'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,'
'0.0.0.0/0,192.168.0.1\n'
'tag:tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n'
'tag:tag0,option:router,192.168.0.1\n'
'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,'
'option:tftp-server,192.168.0.3\n'
'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,'
'option:server-ip-address,192.168.0.2\n'
'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,'
'option:bootfile-name,pxelinux.0\n'
'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,'
'option:tftp-server,192.168.0.5\n'
'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,'
'option:server-ip-address,192.168.0.5\n'
'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,'
'option:bootfile-name,pxelinux2.0\n'
'tag:44444444-4444-4444-4444-444444444444,'
'option:tftp-server,192.168.0.7\n'
'tag:44444444-4444-4444-4444-444444444444,'
'option:server-ip-address,192.168.0.7\n'
'tag:44444444-4444-4444-4444-444444444444,'
'option:bootfile-name,pxelinux3.0')
expected = expected.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,
FakeV4NetworkPxe3Ports("portsDifferent"),
version=dhcp.Dnsmasq.MINIMUM_VERSION)
dm._output_opts_file()
self.safe.assert_called_once_with('/foo/opts', expected)
def test_output_opts_file_pxe_3port_2net(self): def test_output_opts_file_pxe_3port_2net(self):
expected = ( expected = (
'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:dns-server,8.8.8.8\n'
@ -1131,18 +1117,15 @@ tag:tag0,option:router""".lstrip()
).lstrip() ).lstrip()
exp_opt_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts' exp_opt_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts'
fake_v6 = '2001:0200:feed:7ac0::1' fake_v6 = '2001:0200:feed:7ac0::1'
fake_v6_cidr = '2001:0200:feed:7ac0::/64'
exp_opt_data = ( exp_opt_data = (
'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:dns-server,8.8.8.8\n'
'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,'
'0.0.0.0/0,192.168.0.1\n' '0.0.0.0/0,192.168.0.1\n'
'tag:tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n'
'tag:tag0,option:router,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1\n'
'tag:tag1,option:dns-server,%s\n' 'tag:tag1,option6:dns-server,%s\n'
'tag:tag1,option:classless-static-route,%s,%s\n' 'tag:tag1,option6:domain-search,openstacklocal').lstrip() % (
'tag:tag1,249,%s,%s').lstrip() % (fake_v6, '[' + fake_v6 + ']')
fake_v6_cidr, fake_v6,
fake_v6_cidr, fake_v6)
return (exp_host_name, exp_host_data, return (exp_host_name, exp_host_data,
exp_addn_name, exp_addn_data, exp_addn_name, exp_addn_data,
exp_opt_name, exp_opt_data,) exp_opt_name, exp_opt_data,)
@ -1333,8 +1316,8 @@ tag:tag0,option:router""".lstrip()
float(2.65)) float(2.65))
def test_check_fail_version(self): def test_check_fail_version(self):
self._check_version('Dnsmasq version 2.48 Copyright (c)...', with testtools.ExpectedException(SystemExit):
float(2.48)) self._check_version('Dnsmasq version 2.62 Copyright (c)...', 0)
def test_check_version_failed_cmd_execution(self): def test_check_version_failed_cmd_execution(self):
with testtools.ExpectedException(SystemExit): with testtools.ExpectedException(SystemExit):