Merge "Support Stateful and Stateless DHCPv6 by dnsmasq"
This commit is contained in:
commit
8bfa46d9b0
@ -313,10 +313,11 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
ver = re.findall("\d+.\d+", out)[0]
|
||||
is_valid_version = float(ver) >= cls.MINIMUM_VERSION
|
||||
if not is_valid_version:
|
||||
LOG.warning(_('FAILED VERSION REQUIREMENT FOR DNSMASQ. '
|
||||
'DHCP AGENT MAY NOT RUN CORRECTLY! '
|
||||
'Please ensure that its version is %s '
|
||||
'or above!'), cls.MINIMUM_VERSION)
|
||||
LOG.error(_('FAILED VERSION REQUIREMENT FOR DNSMASQ. '
|
||||
'DHCP AGENT MAY NOT RUN CORRECTLY! '
|
||||
'Please ensure that its version is %s '
|
||||
'or above!'), cls.MINIMUM_VERSION)
|
||||
raise SystemExit(1)
|
||||
except (OSError, RuntimeError, IndexError, ValueError):
|
||||
LOG.error(_('Unable to determine dnsmasq version. '
|
||||
'Please ensure that its version is %s '
|
||||
@ -368,17 +369,12 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
else:
|
||||
# Note(scollins) If the IPv6 attributes are not set, set it as
|
||||
# static to preserve previous behavior
|
||||
if (not getattr(subnet, 'ipv6_ra_mode', None) and
|
||||
not getattr(subnet, 'ipv6_address_mode', None)):
|
||||
addr_mode = 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'
|
||||
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)
|
||||
|
||||
@ -390,14 +386,9 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
# mode is optional and is not set - skip it
|
||||
if mode:
|
||||
cmd.append('--dhcp-range=%s%s,%s,%s,%s' %
|
||||
(set_tag, self._TAG_PREFIX % i,
|
||||
('set:', self._TAG_PREFIX % i,
|
||||
cidr.network, mode, lease))
|
||||
else:
|
||||
cmd.append('--dhcp-range=%s%s,%s,%s' %
|
||||
(set_tag, self._TAG_PREFIX % i,
|
||||
cidr.network, lease))
|
||||
|
||||
possible_leases += cidr.size
|
||||
possible_leases += cidr.size
|
||||
|
||||
# Cap the limit because creating lots of subnets can inflate
|
||||
# this possible lease cap.
|
||||
@ -465,9 +456,8 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
# associated with the subnet being managed by this
|
||||
# dhcp agent
|
||||
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
|
||||
if (ra_mode is None and addr_mode == constants.IPV6_SLAAC):
|
||||
if addr_mode != constants.DHCPV6_STATEFUL:
|
||||
continue
|
||||
hostname = 'host-%s' % alloc.ip_address.replace(
|
||||
'.', '-').replace(':', '-')
|
||||
@ -497,7 +487,6 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
|
||||
LOG.debug(_('Building host file: %s'), filename)
|
||||
for (port, alloc, hostname, name) in self._iter_hosts():
|
||||
set_tag = ''
|
||||
# (dzyu) Check if it is legal ipv6 address, if so, need wrap
|
||||
# it with '[]' to let dnsmasq to distinguish MAC address from
|
||||
# IPv6 address.
|
||||
@ -510,12 +499,9 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
"ip": ip_address})
|
||||
|
||||
if getattr(port, 'extra_dhcp_opts', False):
|
||||
if self.version >= self.MINIMUM_VERSION:
|
||||
set_tag = 'set:'
|
||||
|
||||
buf.write('%s,%s,%s,%s%s\n' %
|
||||
(port.mac_address, name, ip_address,
|
||||
set_tag, port.id))
|
||||
'set:', port.id))
|
||||
else:
|
||||
buf.write('%s,%s,%s\n' %
|
||||
(port.mac_address, name, ip_address))
|
||||
@ -575,17 +561,27 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
dhcp_ips = collections.defaultdict(list)
|
||||
subnet_idx_map = {}
|
||||
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
|
||||
if subnet.dns_nameservers:
|
||||
options.append(
|
||||
self._format_option(i, 'dns-server',
|
||||
','.join(subnet.dns_nameservers)))
|
||||
self._format_option(
|
||||
subnet.ip_version, i, 'dns-server',
|
||||
','.join(
|
||||
Dnsmasq._convert_to_literal_addrs(
|
||||
subnet.ip_version, subnet.dns_nameservers))))
|
||||
else:
|
||||
# use the dnsmasq ip as nameservers only if there is no
|
||||
# dns-server submitted by the server
|
||||
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
|
||||
host_routes = []
|
||||
for hr in subnet.host_routes:
|
||||
@ -603,27 +599,42 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
'%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 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:
|
||||
options.append(self._format_option(i, 'router', gateway))
|
||||
options.append(self._format_option(subnet.ip_version,
|
||||
i, 'router',
|
||||
gateway))
|
||||
else:
|
||||
options.append(self._format_option(i, 'router'))
|
||||
options.append(self._format_option(subnet.ip_version,
|
||||
i, 'router'))
|
||||
|
||||
for port in self.network.ports:
|
||||
if getattr(port, 'extra_dhcp_opts', False):
|
||||
options.extend(
|
||||
self._format_option(port.id, opt.opt_name, opt.opt_value)
|
||||
for opt in port.extra_dhcp_opts)
|
||||
for ip_version in (4, 6):
|
||||
if any(
|
||||
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
|
||||
# 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)
|
||||
|
||||
for i, ips in dhcp_ips.items():
|
||||
if len(ips) > 1:
|
||||
options.append(self._format_option(i,
|
||||
'dns-server',
|
||||
','.join(ips)))
|
||||
for ip_version in (4, 6):
|
||||
vx_ips = [ip for ip in ips
|
||||
if netaddr.IPAddress(ip).version == ip_version]
|
||||
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')
|
||||
utils.replace_file(name, '\n'.join(options))
|
||||
@ -667,22 +684,26 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
|
||||
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."""
|
||||
if self.version >= self.MINIMUM_VERSION:
|
||||
set_tag = 'tag:'
|
||||
else:
|
||||
set_tag = ''
|
||||
|
||||
option = str(option)
|
||||
|
||||
if isinstance(tag, int):
|
||||
tag = self._TAG_PREFIX % tag
|
||||
|
||||
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):
|
||||
'''Determine if the metadata route will be pushed to hosts on subnet.
|
||||
|
@ -17,6 +17,7 @@ import contextlib
|
||||
import os
|
||||
|
||||
import mock
|
||||
import netaddr
|
||||
from oslo.config import cfg
|
||||
import testtools
|
||||
|
||||
@ -60,8 +61,8 @@ class FakePort2:
|
||||
id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
|
||||
admin_state_up = False
|
||||
device_owner = 'foo2'
|
||||
fixed_ips = [FakeIPAllocation('fdca:3ba5:a17a:4ba3::2',
|
||||
'ffffffff-ffff-ffff-ffff-ffffffffffff')]
|
||||
fixed_ips = [FakeIPAllocation('192.168.0.3',
|
||||
'dddddddd-dddd-dddd-dddd-dddddddddddd')]
|
||||
mac_address = '00:00:f3:aa:bb:cc'
|
||||
|
||||
def __init__(self):
|
||||
@ -72,10 +73,10 @@ class FakePort3:
|
||||
id = '44444444-4444-4444-4444-444444444444'
|
||||
admin_state_up = True
|
||||
device_owner = 'foo3'
|
||||
fixed_ips = [FakeIPAllocation('192.168.0.3',
|
||||
fixed_ips = [FakeIPAllocation('192.168.0.4',
|
||||
'dddddddd-dddd-dddd-dddd-dddddddddddd'),
|
||||
FakeIPAllocation('fdca:3ba5:a17a:4ba3::3',
|
||||
'ffffffff-ffff-ffff-ffff-ffffffffffff')]
|
||||
FakeIPAllocation('192.168.1.2',
|
||||
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')]
|
||||
mac_address = '00:00:0f:aa:bb:cc'
|
||||
|
||||
def __init__(self):
|
||||
@ -95,6 +96,32 @@ class FakePort4:
|
||||
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:
|
||||
id = 'rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr'
|
||||
admin_state_up = True
|
||||
@ -224,6 +251,18 @@ class FakeV4SubnetNoDHCP:
|
||||
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:
|
||||
id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
|
||||
ip_version = 6
|
||||
@ -271,14 +310,14 @@ class FakeV6Network:
|
||||
|
||||
class FakeDualNetwork:
|
||||
id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
|
||||
subnets = [FakeV4Subnet(), FakeV6Subnet()]
|
||||
ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()]
|
||||
subnets = [FakeV4Subnet(), FakeV6SubnetDHCPStateful()]
|
||||
ports = [FakePort1(), FakeV6Port(), FakeDualPort(), FakeRouterPort()]
|
||||
namespace = 'qdhcp-ns'
|
||||
|
||||
|
||||
class FakeDualNetworkGatewayRoute:
|
||||
id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
|
||||
subnets = [FakeV4SubnetGatewayRoute(), FakeV6Subnet()]
|
||||
subnets = [FakeV4SubnetGatewayRoute(), FakeV6SubnetDHCPStateful()]
|
||||
ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()]
|
||||
namespace = 'qdhcp-ns'
|
||||
|
||||
@ -724,11 +763,16 @@ class TestDnsmasq(TestBase):
|
||||
prefix = '--dhcp-range=set:tag%d,%s,static,%s%s'
|
||||
else:
|
||||
prefix = '--dhcp-range=set:tag%d,%s,%s%s'
|
||||
expected.extend(prefix %
|
||||
(i, s.cidr.split('/')[0], lease_duration, seconds)
|
||||
for i, s in enumerate(network.subnets))
|
||||
possible_leases = 0
|
||||
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)
|
||||
|
||||
self.execute.return_value = ('', '')
|
||||
@ -775,10 +819,9 @@ class TestDnsmasq(TestBase):
|
||||
self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_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()
|
||||
subnet = FakeV6Subnet()
|
||||
subnet.ipv6_ra_mode = True
|
||||
subnet = FakeV6SubnetSlaac()
|
||||
network.subnets = [subnet]
|
||||
self._test_spawn(['--conf-file=', '--domain=openstacklocal'],
|
||||
network, has_static=False)
|
||||
@ -805,18 +848,15 @@ class TestDnsmasq(TestBase):
|
||||
|
||||
def test_output_opts_file(self):
|
||||
fake_v6 = '2001:0200:feed:7ac0::1'
|
||||
fake_v6_cidr = '2001:0200:feed:7ac0::/64'
|
||||
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:tag1,option:dns-server,%s\n'
|
||||
'tag:tag1,option:classless-static-route,%s,%s\n'
|
||||
'tag:tag1,249,%s,%s').lstrip() % (fake_v6,
|
||||
fake_v6_cidr, fake_v6,
|
||||
fake_v6_cidr, fake_v6)
|
||||
'tag:tag1,option6:dns-server,%s\n'
|
||||
'tag:tag1,option6:domain-search,openstacklocal').lstrip() % (
|
||||
'[' + fake_v6 + ']')
|
||||
|
||||
with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
|
||||
conf_fn.return_value = '/foo/opts'
|
||||
@ -828,15 +868,12 @@ class TestDnsmasq(TestBase):
|
||||
|
||||
def test_output_opts_file_gateway_route(self):
|
||||
fake_v6 = '2001:0200:feed:7ac0::1'
|
||||
fake_v6_cidr = '2001:0200:feed:7ac0::/64'
|
||||
expected = """
|
||||
tag:tag0,option:dns-server,8.8.8.8
|
||||
tag:tag0,option:router,192.168.0.1
|
||||
tag:tag1,option:dns-server,%s
|
||||
tag:tag1,option:classless-static-route,%s,%s
|
||||
tag:tag1,249,%s,%s""".lstrip() % (fake_v6,
|
||||
fake_v6_cidr, fake_v6,
|
||||
fake_v6_cidr, fake_v6)
|
||||
tag:tag1,option6:dns-server,%s
|
||||
tag:tag1,option6:domain-search,openstacklocal""".lstrip() % (
|
||||
'[' + fake_v6 + ']')
|
||||
|
||||
with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
|
||||
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)
|
||||
|
||||
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):
|
||||
expected = """
|
||||
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)
|
||||
|
||||
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):
|
||||
expected = (
|
||||
'tag:tag0,option:dns-server,8.8.8.8\n'
|
||||
@ -1131,18 +1117,15 @@ tag:tag0,option:router""".lstrip()
|
||||
).lstrip()
|
||||
exp_opt_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts'
|
||||
fake_v6 = '2001:0200:feed:7ac0::1'
|
||||
fake_v6_cidr = '2001:0200:feed:7ac0::/64'
|
||||
exp_opt_data = (
|
||||
'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:tag1,option:dns-server,%s\n'
|
||||
'tag:tag1,option:classless-static-route,%s,%s\n'
|
||||
'tag:tag1,249,%s,%s').lstrip() % (fake_v6,
|
||||
fake_v6_cidr, fake_v6,
|
||||
fake_v6_cidr, fake_v6)
|
||||
'tag:tag1,option6:dns-server,%s\n'
|
||||
'tag:tag1,option6:domain-search,openstacklocal').lstrip() % (
|
||||
'[' + fake_v6 + ']')
|
||||
return (exp_host_name, exp_host_data,
|
||||
exp_addn_name, exp_addn_data,
|
||||
exp_opt_name, exp_opt_data,)
|
||||
@ -1333,8 +1316,8 @@ tag:tag0,option:router""".lstrip()
|
||||
float(2.65))
|
||||
|
||||
def test_check_fail_version(self):
|
||||
self._check_version('Dnsmasq version 2.48 Copyright (c)...',
|
||||
float(2.48))
|
||||
with testtools.ExpectedException(SystemExit):
|
||||
self._check_version('Dnsmasq version 2.62 Copyright (c)...', 0)
|
||||
|
||||
def test_check_version_failed_cmd_execution(self):
|
||||
with testtools.ExpectedException(SystemExit):
|
||||
|
Loading…
Reference in New Issue
Block a user