DVR edge router: avoid accidental centralized floating IP remove
Need to pass centralized floating IPs as preserve_ips to
_external_gateway_added during DVR router update.
Otherwise IP addresses will be deleted from gw device in certain case.
The case is when a router with active centralized floating IPs is
being scheduled to a new dvr_snat L3 agent (rescheduled from a down one).
Please see corresponding traces in the bug description.
Change-Id: Iaeb9fbed73144df6fcd9092c665ed19986e85f4d
Closes-bug: #1817306
(cherry picked from commit 1ee18775a9
)
This commit is contained in:
parent
8b255a648c
commit
a906ace3ef
|
@ -21,6 +21,7 @@ from neutron.agent.l3 import router_info as router
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import iptables_manager
|
from neutron.agent.linux import iptables_manager
|
||||||
from neutron.common import constants as n_const
|
from neutron.common import constants as n_const
|
||||||
|
from neutron.common import utils as common_utils
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -55,6 +56,15 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter):
|
||||||
"current dvr_snat host.", self.snat_namespace.name)
|
"current dvr_snat host.", self.snat_namespace.name)
|
||||||
self.external_gateway_removed(ex_gw_port, interface_name)
|
self.external_gateway_removed(ex_gw_port, interface_name)
|
||||||
|
|
||||||
|
def _list_centralized_floating_ip_cidrs(self):
|
||||||
|
# Compute a list of addresses this gw is supposed to have.
|
||||||
|
# This avoids unnecessarily removing those addresses and
|
||||||
|
# causing a momentarily network outage.
|
||||||
|
floating_ips = self.get_floating_ips()
|
||||||
|
return [common_utils.ip_to_cidr(ip['floating_ip_address'])
|
||||||
|
for ip in floating_ips
|
||||||
|
if ip.get(n_const.DVR_SNAT_BOUND)]
|
||||||
|
|
||||||
def external_gateway_updated(self, ex_gw_port, interface_name):
|
def external_gateway_updated(self, ex_gw_port, interface_name):
|
||||||
if not self._is_this_snat_host():
|
if not self._is_this_snat_host():
|
||||||
# no centralized SNAT gateway for this node/agent
|
# no centralized SNAT gateway for this node/agent
|
||||||
|
@ -71,10 +81,11 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter):
|
||||||
# newly created gateway
|
# newly created gateway
|
||||||
return self.external_gateway_added(ex_gw_port, interface_name)
|
return self.external_gateway_added(ex_gw_port, interface_name)
|
||||||
else:
|
else:
|
||||||
|
preserve_ips = self._list_centralized_floating_ip_cidrs()
|
||||||
self._external_gateway_added(ex_gw_port,
|
self._external_gateway_added(ex_gw_port,
|
||||||
interface_name,
|
interface_name,
|
||||||
self.snat_namespace.name,
|
self.snat_namespace.name,
|
||||||
preserve_ips=[])
|
preserve_ips)
|
||||||
|
|
||||||
def _external_gateway_removed(self, ex_gw_port, interface_name):
|
def _external_gateway_removed(self, ex_gw_port, interface_name):
|
||||||
super(DvrEdgeRouter, self).external_gateway_removed(ex_gw_port,
|
super(DvrEdgeRouter, self).external_gateway_removed(ex_gw_port,
|
||||||
|
|
|
@ -750,6 +750,47 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
||||||
def test_external_gateway_updated_dual_stack(self):
|
def test_external_gateway_updated_dual_stack(self):
|
||||||
self._test_external_gateway_updated(dual_stack=True)
|
self._test_external_gateway_updated(dual_stack=True)
|
||||||
|
|
||||||
|
def test_external_gateway_updated_dvr(self):
|
||||||
|
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
||||||
|
agent.conf.agent_mode = 'dvr_snat'
|
||||||
|
agent.host = HOSTNAME
|
||||||
|
router = l3_test_common.prepare_router_data(num_internal_ports=2)
|
||||||
|
router['distributed'] = True
|
||||||
|
router['gw_port_host'] = HOSTNAME
|
||||||
|
self._set_ri_kwargs(agent, router['id'], router)
|
||||||
|
ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs)
|
||||||
|
ri._create_dvr_gateway = mock.Mock()
|
||||||
|
ri.get_snat_interfaces = mock.Mock(return_value=self.snat_ports)
|
||||||
|
ri.snat_ports = self.snat_ports
|
||||||
|
ri._create_snat_namespace()
|
||||||
|
ex_net_id = _uuid()
|
||||||
|
ri.fip_ns = agent.get_fip_ns(ex_net_id)
|
||||||
|
ri.internal_ports = self.snat_ports
|
||||||
|
ri.use_ipv6 = False
|
||||||
|
interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test(
|
||||||
|
self, ri)
|
||||||
|
|
||||||
|
fake_fip = {'floatingips': [{'id': _uuid(),
|
||||||
|
'floating_ip_address': '192.168.1.34',
|
||||||
|
'fixed_ip_address': '192.168.0.1',
|
||||||
|
'port_id': _uuid(),
|
||||||
|
'dvr_snat_bound': True}]}
|
||||||
|
router[lib_constants.FLOATINGIP_KEY] = fake_fip['floatingips']
|
||||||
|
ri.external_gateway_updated(ex_gw_port, interface_name)
|
||||||
|
self.assertEqual(1, self.mock_driver.plug.call_count)
|
||||||
|
self.assertEqual(1, self.mock_driver.init_router_port.call_count)
|
||||||
|
exp_arp_calls = [mock.call(ri.snat_namespace.name, interface_name,
|
||||||
|
'20.0.0.30')]
|
||||||
|
self.send_adv_notif.assert_has_calls(exp_arp_calls)
|
||||||
|
ip_cidrs = ['20.0.0.30/24']
|
||||||
|
kwargs = {'preserve_ips': ['192.168.1.34/32'],
|
||||||
|
'namespace': ri.snat_namespace.name,
|
||||||
|
'extra_subnets': [{'cidr': '172.16.0.0/24'}],
|
||||||
|
'clean_connections': True}
|
||||||
|
self.mock_driver.init_router_port.assert_called_with(interface_name,
|
||||||
|
ip_cidrs,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
def test_dvr_edge_router_init_for_snat_namespace_object(self):
|
def test_dvr_edge_router_init_for_snat_namespace_object(self):
|
||||||
router = {'id': _uuid()}
|
router = {'id': _uuid()}
|
||||||
self._set_ri_kwargs(mock.Mock(), router['id'], router)
|
self._set_ri_kwargs(mock.Mock(), router['id'], router)
|
||||||
|
|
Loading…
Reference in New Issue