]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Fix extra-dhcp-opt on stateless dhcpv6 subnet
authorlijianlj <lijianlj@cn.ibm.com>
Fri, 16 Jan 2015 09:02:40 +0000 (17:02 +0800)
committerlijianlj <lijianlj@cn.ibm.com>
Wed, 4 Feb 2015 02:04:08 +0000 (10:04 +0800)
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
neutron/tests/unit/test_linux_dhcp.py

index 5906aad6208725fd5c7f306a7fec8bfbf135e4d0..63a61d8a7b10e894ed6c00543e250889f9440b0c 100644 (file)
@@ -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
index 84f66bed589615abea5bc7c291ac6474f41186e4..361bda941195726bfc7426f639938e045dc9bfa1 100644 (file)
@@ -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,