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:
Oleg Bondarev 2019-02-22 16:00:30 +04:00
parent 119ef92b47
commit c620b3c91e
2 changed files with 53 additions and 1 deletions

View File

@ -20,6 +20,7 @@ from neutron.agent.l3 import dvr_snat_ns
from neutron.agent.l3 import router_info as router
from neutron.agent.linux import ip_lib
from neutron.agent.linux import iptables_manager
from neutron.common import utils as common_utils
LOG = logging.getLogger(__name__)
@ -54,6 +55,15 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter):
"current dvr_snat host.", self.snat_namespace.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(lib_constants.DVR_SNAT_BOUND)]
def external_gateway_updated(self, ex_gw_port, interface_name):
if not self._is_this_snat_host():
# no centralized SNAT gateway for this node/agent
@ -70,10 +80,11 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter):
# newly created gateway
return self.external_gateway_added(ex_gw_port, interface_name)
else:
preserve_ips = self._list_centralized_floating_ip_cidrs()
self._external_gateway_added(ex_gw_port,
interface_name,
self.snat_namespace.name,
preserve_ips=[])
preserve_ips)
def _external_gateway_removed(self, ex_gw_port, interface_name):
super(DvrEdgeRouter, self).external_gateway_removed(ex_gw_port,

View File

@ -766,6 +766,47 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
def test_external_gateway_updated_dual_stack(self):
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):
router = {'id': _uuid()}
self._set_ri_kwargs(mock.Mock(), router['id'], router)