From 29250949012e9c0a60b0ddb56ddbf18d7b68106b Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Fri, 3 Oct 2014 17:32:01 -0400 Subject: [PATCH] Teach DHCP Agent about DVR router interfaces When DVR is enabled and enable_isolated_metadata=True, the DHCP agent should only inject a metadata host route when there is no port with the gateway IP address configured on the subnet. Add a check for DEVICE_OWNER_DVR_INTERFACE when we look at each port's device_owner field, otherwise it will always add this route to the opts file when DVR is enabled. Change-Id: I3ff3bb85105b8215b36535983016d8c0ff3d8cb7 Closes-bug: #1377307 --- neutron/agent/linux/dhcp.py | 3 +- neutron/tests/unit/test_dhcp_agent.py | 53 ++++++++++++++++++++++++--- neutron/tests/unit/test_linux_dhcp.py | 30 ++++++++++++++- 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index aba75af5716..51e89913d48 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -730,7 +730,8 @@ class Dnsmasq(DhcpLocalProcess): subnets = dict((subnet.id, subnet) for subnet in network.subnets) for port in network.ports: - if port.device_owner != constants.DEVICE_OWNER_ROUTER_INTF: + if port.device_owner not in (constants.DEVICE_OWNER_ROUTER_INTF, + constants.DEVICE_OWNER_DVR_INTERFACE): continue for alloc in port.fixed_ips: if subnets[alloc.subnet_id].gateway_ip == alloc.ip_address: diff --git a/neutron/tests/unit/test_dhcp_agent.py b/neutron/tests/unit/test_dhcp_agent.py index c6f98643fe8..cae87c692b5 100644 --- a/neutron/tests/unit/test_dhcp_agent.py +++ b/neutron/tests/unit/test_dhcp_agent.py @@ -72,6 +72,8 @@ fake_meta_subnet = dhcp.DictModel(dict(id='bbbbbbbb-1111-2222-bbbbbbbbbbbb', fake_fixed_ip1 = dhcp.DictModel(dict(id='', subnet_id=fake_subnet1.id, ip_address='172.9.9.9')) +fake_fixed_ip2 = dhcp.DictModel(dict(id='', subnet_id=fake_subnet1.id, + ip_address='172.9.9.10')) fake_meta_fixed_ip = dhcp.DictModel(dict(id='', subnet=fake_meta_subnet, ip_address='169.254.169.254')) fake_allocation_pool_subnet1 = dhcp.DictModel(dict(id='', start='172.9.9.2', @@ -89,7 +91,7 @@ fake_port2 = dhcp.DictModel(dict(id='12345678-1234-aaaa-123456789000', device_owner='', mac_address='aa:bb:cc:dd:ee:99', network_id='12345678-1234-5678-1234567890ab', - fixed_ips=[])) + fixed_ips=[fake_fixed_ip2])) fake_meta_port = dhcp.DictModel(dict(id='12345678-1234-aaaa-1234567890ab', mac_address='aa:bb:cc:dd:ee:ff', @@ -98,6 +100,13 @@ fake_meta_port = dhcp.DictModel(dict(id='12345678-1234-aaaa-1234567890ab', device_id='forzanapoli', fixed_ips=[fake_meta_fixed_ip])) +fake_dist_port = dhcp.DictModel(dict(id='12345678-1234-aaaa-1234567890ab', + mac_address='aa:bb:cc:dd:ee:ff', + network_id='12345678-1234-5678-1234567890ab', + device_owner=const.DEVICE_OWNER_DVR_INTERFACE, + device_id='forzanapoli', + fixed_ips=[fake_meta_fixed_ip])) + fake_network = dhcp.NetModel(True, dict(id='12345678-1234-5678-1234567890ab', tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', admin_state_up=True, @@ -112,6 +121,14 @@ isolated_network = dhcp.NetModel( subnets=[fake_subnet1], ports=[fake_port1])) +nonisolated_dist_network = dhcp.NetModel( + True, dict( + id='12345678-1234-5678-1234567890ab', + tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', + admin_state_up=True, + subnets=[fake_subnet1], + ports=[fake_port1, fake_port2])) + empty_network = dhcp.NetModel( True, dict( id='12345678-1234-5678-1234567890ab', @@ -127,6 +144,13 @@ fake_meta_network = dhcp.NetModel( subnets=[fake_meta_subnet], ports=[fake_meta_port])) +fake_dist_network = dhcp.NetModel( + True, dict(id='12345678-1234-5678-1234567890ab', + tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', + admin_state_up=True, + subnets=[fake_meta_subnet], + ports=[fake_meta_port, fake_dist_port])) + fake_down_network = dhcp.NetModel( True, dict(id='12345678-dddd-dddd-1234567890ab', tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', @@ -540,13 +564,26 @@ class TestDhcpAgentEventHandler(base.BaseTestCase): def test_enable_dhcp_helper_enable_metadata_nonisolated_network(self): nonisolated_network = copy.deepcopy(isolated_network) - nonisolated_network.ports[0].device_owner = "network:router_interface" + nonisolated_network.ports[0].device_owner = ( + const.DEVICE_OWNER_ROUTER_INTF) nonisolated_network.ports[0].fixed_ips[0].ip_address = '172.9.9.1' self._enable_dhcp_helper(nonisolated_network, enable_isolated_metadata=True, is_isolated_network=False) + def test_enable_dhcp_helper_enable_metadata_nonisolated_dist_network(self): + nonisolated_dist_network.ports[0].device_owner = ( + const.DEVICE_OWNER_ROUTER_INTF) + nonisolated_dist_network.ports[0].fixed_ips[0].ip_address = '172.9.9.1' + nonisolated_dist_network.ports[1].device_owner = ( + const.DEVICE_OWNER_DVR_INTERFACE) + nonisolated_dist_network.ports[1].fixed_ips[0].ip_address = '172.9.9.1' + + self._enable_dhcp_helper(nonisolated_dist_network, + enable_isolated_metadata=True, + is_isolated_network=False) + def test_enable_dhcp_helper_enable_metadata_empty_network(self): self._enable_dhcp_helper(empty_network, enable_isolated_metadata=True, @@ -685,7 +722,7 @@ class TestDhcpAgentEventHandler(base.BaseTestCase): mock.call().disable() ]) - def test_enable_isolated_metadata_proxy_with_metadata_network(self): + def _test_metadata_network(self, network): cfg.CONF.set_override('enable_metadata_network', True) cfg.CONF.set_override('debug', True) cfg.CONF.set_override('verbose', False) @@ -695,7 +732,7 @@ class TestDhcpAgentEventHandler(base.BaseTestCase): # Ensure the mock is restored if this test fail try: with mock.patch(class_path) as ip_wrapper: - self.dhcp.enable_isolated_metadata_proxy(fake_meta_network) + self.dhcp.enable_isolated_metadata_proxy(network) ip_wrapper.assert_has_calls([mock.call( 'sudo', 'qdhcp-12345678-1234-5678-1234567890ab'), @@ -708,11 +745,17 @@ class TestDhcpAgentEventHandler(base.BaseTestCase): mock.ANY, '--debug', ('--log-file=neutron-ns-metadata-proxy-%s.log' % - fake_meta_network.id)], addl_env=None) + network.id)], addl_env=None) ]) finally: self.external_process_p.start() + def test_enable_isolated_metadata_proxy_with_metadata_network(self): + self._test_metadata_network(fake_meta_network) + + def test_enable_isolated_metadata_proxy_with_dist_network(self): + self._test_metadata_network(fake_dist_network) + def test_network_create_end(self): payload = dict(network=dict(id=fake_network.id)) diff --git a/neutron/tests/unit/test_linux_dhcp.py b/neutron/tests/unit/test_linux_dhcp.py index b3090e1b575..a0187369b82 100644 --- a/neutron/tests/unit/test_linux_dhcp.py +++ b/neutron/tests/unit/test_linux_dhcp.py @@ -130,8 +130,9 @@ class FakeRouterPort: 'dddddddd-dddd-dddd-dddd-dddddddddddd')] mac_address = '00:00:0f:rr:rr:rr' - def __init__(self): + def __init__(self, dev_owner=constants.DEVICE_OWNER_ROUTER_INTF): self.extra_dhcp_opts = [] + self.device_owner = dev_owner class FakePortMultipleAgents1: @@ -341,6 +342,13 @@ class FakeV4NetworkNoRouter: ports = [FakePort1()] +class FakeV4NetworkDistRouter: + id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' + subnets = [FakeV4Subnet()] + ports = [FakePort1(), + FakeRouterPort(dev_owner=constants.DEVICE_OWNER_DVR_INTERFACE)] + + class FakeDualV4Pxe3Ports: id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' subnets = [FakeV4Subnet(), FakeV4SubnetNoDHCP()] @@ -962,6 +970,26 @@ tag:tag0,option:router""".lstrip() self.safe.assert_called_once_with('/foo/opts', expected) + def test_output_opts_file_dist_neutron_router_on_subnet(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').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, FakeV4NetworkDistRouter(), + version=dhcp.Dnsmasq.MINIMUM_VERSION) + with mock.patch.object(dm, '_make_subnet_interface_ip_map') as ipm: + ipm.return_value = {FakeV4Subnet.id: '192.168.0.1'} + + dm._output_opts_file() + self.assertTrue(ipm.called) + + self.safe.assert_called_once_with('/foo/opts', expected) + def test_output_opts_file_pxe_2port_1net(self): expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n'