From a584e35775391a2a93535482c5a05ff8774cb56b Mon Sep 17 00:00:00 2001 From: lijianlj Date: Fri, 16 Jan 2015 17:02:40 +0800 Subject: [PATCH] Fix extra-dhcp-opt on stateless dhcpv6 subnet The opts in dnsmasq opt-file is depending on the tag set in the host-file. When you have only one stateless dhcpv6 subnet, it will be filtered by the _iter_host() function, and will not be written to the host-file, this may lead to extra-dhcp-opt not working. Closes-Bug:#1411137 Change-Id: Ieed16280fb3c4c91f7d906c0410446d2a112b93e --- neutron/agent/linux/dhcp.py | 31 +++++++++++++---- neutron/tests/unit/test_linux_dhcp.py | 49 +++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index 5906aad62..63a61d8a7 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -435,6 +435,8 @@ class Dnsmasq(DhcpLocalProcess): ( port, # a DictModel instance representing the port. alloc, # a DictModel instance of the allocated ip and subnet. + # if alloc is None, it means there is no need to allocate + # an IPv6 address because of stateless DHCPv6 network. host_name, # Host name. name, # Canonical hostname in the format 'hostname[.domain]'. ) @@ -448,8 +450,13 @@ class Dnsmasq(DhcpLocalProcess): # dhcp agent if alloc.subnet_id in v6_nets: addr_mode = v6_nets[alloc.subnet_id].ipv6_address_mode - if addr_mode != constants.DHCPV6_STATEFUL: + if addr_mode == constants.IPV6_SLAAC: continue + elif addr_mode == constants.DHCPV6_STATELESS: + alloc = hostname = fqdn = None + yield (port, alloc, hostname, fqdn) + continue + hostname = 'host-%s' % alloc.ip_address.replace( '.', '-').replace(':', '-') fqdn = hostname @@ -480,6 +487,14 @@ class Dnsmasq(DhcpLocalProcess): dhcp_enabled_subnet_ids = [s.id for s in self.network.subnets if s.enable_dhcp] for (port, alloc, hostname, name) in self._iter_hosts(): + if not alloc: + if getattr(port, 'extra_dhcp_opts', False): + buf.write('%s,%s%s\n' % + (port.mac_address, 'set:', port.id)) + LOG.debug('Adding %(mac)s : set:%(tag)s', + {"mac": port.mac_address, "tag": port.id}) + continue + # don't write ip address which belongs to a dhcp disabled subnet. if alloc.subnet_id not in dhcp_enabled_subnet_ids: continue @@ -491,17 +506,20 @@ class Dnsmasq(DhcpLocalProcess): if netaddr.valid_ipv6(ip_address): ip_address = '[%s]' % ip_address - LOG.debug('Adding %(mac)s : %(name)s : %(ip)s', - {"mac": port.mac_address, "name": name, - "ip": ip_address}) - if getattr(port, 'extra_dhcp_opts', False): buf.write('%s,%s,%s,%s%s\n' % (port.mac_address, name, ip_address, 'set:', port.id)) + LOG.debug('Adding %(mac)s : %(name)s : %(ip)s : ' + 'set:%(tag)s', + {"mac": port.mac_address, "name": name, + "ip": ip_address, "tag": port.id}) else: buf.write('%s,%s,%s\n' % (port.mac_address, name, ip_address)) + LOG.debug('Adding %(mac)s : %(name)s : %(ip)s', + {"mac": port.mac_address, "name": name, + "ip": ip_address}) utils.replace_file(filename, buf.getvalue()) LOG.debug('Done building host file %s', filename) @@ -542,7 +560,8 @@ class Dnsmasq(DhcpLocalProcess): for (port, alloc, hostname, fqdn) in self._iter_hosts(): # It is compulsory to write the `fqdn` before the `hostname` in # order to obtain it in PTR responses. - buf.write('%s\t%s %s\n' % (alloc.ip_address, fqdn, hostname)) + if alloc: + buf.write('%s\t%s %s\n' % (alloc.ip_address, fqdn, hostname)) addn_hosts = self.get_conf_file_name('addn_hosts') utils.replace_file(addn_hosts, buf.getvalue()) return addn_hosts diff --git a/neutron/tests/unit/test_linux_dhcp.py b/neutron/tests/unit/test_linux_dhcp.py index 84f66bed5..361bda941 100644 --- a/neutron/tests/unit/test_linux_dhcp.py +++ b/neutron/tests/unit/test_linux_dhcp.py @@ -111,6 +111,21 @@ class FakeV6Port(object): self.extra_dhcp_opts = [] +class FakeV6PortExtraOpt(object): + id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh' + admin_state_up = True + device_owner = 'foo3' + fixed_ips = [FakeIPAllocation('ffea:3ba5:a17a:4ba3:0216:3eff:fec2:771d', + 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')] + mac_address = '00:16:3e:c2:77:1d' + + def __init__(self): + self.extra_dhcp_opts = [ + DhcpOpt(opt_name='dns-server', + opt_value='ffea:3ba5:a17a:4ba3::100', + ip_version=6)] + + class FakeDualPort(object): id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh' admin_state_up = True @@ -289,6 +304,18 @@ class FakeV6SubnetSlaac(object): ipv6_ra_mode = None +class FakeV6SubnetStateless(object): + id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' + ip_version = 6 + cidr = 'ffea:3ba5:a17a:4ba3::/64' + gateway_ip = 'ffea:3ba5:a17a:4ba3::1' + enable_dhcp = True + dns_nameservers = [] + host_routes = [] + ipv6_address_mode = constants.DHCPV6_STATELESS + ipv6_ra_mode = None + + class FakeV4SubnetNoGateway(object): id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' ip_version = 4 @@ -512,6 +539,14 @@ class FakeV4NetworkMultipleTags(object): DhcpOpt(opt_name='tag:ipxe,bootfile-name', opt_value='pxelinux.0')] +class FakeV6NetworkStatelessDHCP(object): + id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' + + subnets = [FakeV6SubnetStateless()] + ports = [FakeV6PortExtraOpt()] + namespace = 'qdhcp-ns' + + class LocalChild(dhcp.DhcpLocalProcess): PORTS = {4: [4], 6: [6]} @@ -1340,6 +1375,20 @@ class TestDnsmasq(TestBase): self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data)]) + def test_host_and_opts_file_on_stateless_dhcpv6_network(self): + exp_host_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/host' + exp_host_data = ('00:16:3e:c2:77:1d,' + 'set:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh\n').lstrip() + exp_opt_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/opts' + exp_opt_data = ('tag:tag0,option6:domain-search,openstacklocal\n' + 'tag:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' + 'option6:dns-server,ffea:3ba5:a17a:4ba3::100').lstrip() + dm = self._get_dnsmasq(FakeV6NetworkStatelessDHCP()) + dm._output_hosts_file() + dm._output_opts_file() + self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data), + mock.call(exp_opt_name, exp_opt_data)]) + def test_should_enable_metadata_namespaces_disabled_returns_false(self): self.conf.set_override('use_namespaces', False) self.assertFalse(dhcp.Dnsmasq.should_enable_metadata(self.conf, -- 2.45.2