diff --git a/neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py b/neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py index 6bbe4f6b87..e07f814d47 100644 --- a/neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py +++ b/neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py @@ -257,6 +257,14 @@ class HaproxyNSDriver(agent_device_driver.AgentDeviceDriver): self.vif_driver.init_l3(interface_name, cidrs, namespace=namespace) gw_ip = port['fixed_ips'][0]['subnet'].get('gateway_ip') + + if not gw_ip: + host_routes = port['fixed_ips'][0]['subnet'].get('host_routes', []) + for host_route in host_routes: + if host_route['destination'] == "0.0.0.0/0": + gw_ip = host_route['nexthop'] + break + if gw_ip: cmd = ['route', 'add', 'default', 'gw', gw_ip] ip_wrapper = ip_lib.IPWrapper(self.root_helper, diff --git a/neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_namespace_driver.py b/neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_namespace_driver.py index 6d96bd550b..e328212d17 100644 --- a/neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_namespace_driver.py +++ b/neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_namespace_driver.py @@ -322,6 +322,43 @@ class TestHaproxyNSDriver(base.BaseTestCase): self.assertRaises(exceptions.PreexistingDeviceFailure, self.driver._plug, 'test_ns', test_port, False) + def test_plug_gw_in_host_routes(self): + test_port = {'id': 'port_id', + 'network_id': 'net_id', + 'mac_address': 'mac_addr', + 'fixed_ips': [{'ip_address': '10.0.0.2', + 'subnet': {'cidr': '10.0.0.0/24', + 'host_routes': + [{'destination': '0.0.0.0/0', + 'nexthop': '10.0.0.1'}]}}]} + with contextlib.nested( + mock.patch('neutron.agent.linux.ip_lib.device_exists'), + mock.patch('netaddr.IPNetwork'), + mock.patch('neutron.agent.linux.ip_lib.IPWrapper'), + ) as (dev_exists, ip_net, ip_wrap): + self.vif_driver.get_device_name.return_value = 'test_interface' + dev_exists.return_value = False + ip_net.return_value = ip_net + ip_net.prefixlen = 24 + + self.driver._plug('test_ns', test_port) + self.rpc_mock.plug_vip_port.assert_called_once_with( + test_port['id']) + self.assertTrue(dev_exists.called) + self.vif_driver.plug.assert_called_once_with('net_id', 'port_id', + 'test_interface', + 'mac_addr', + namespace='test_ns') + self.vif_driver.init_l3.assert_called_once_with('test_interface', + ['10.0.0.2/24'], + namespace= + 'test_ns') + cmd = ['route', 'add', 'default', 'gw', '10.0.0.1'] + ip_wrap.assert_has_calls([ + mock.call('sudo_test', namespace='test_ns'), + mock.call().netns.execute(cmd, check_exit_code=False), + ]) + def test_unplug(self): self.vif_driver.get_device_name.return_value = 'test_interface'