Merge "Populate self.floating_ips_dict using "ip rule" information" into stable/train

This commit is contained in:
Zuul 2021-10-08 10:41:14 +00:00 committed by Gerrit Code Review
commit d75e19cfda
6 changed files with 116 additions and 58 deletions

View File

@ -46,27 +46,63 @@ class DvrLocalRouter(dvr_router_base.DvrRouterBase):
self.rtr_fip_connect = False self.rtr_fip_connect = False
self.fip_ns = None self.fip_ns = None
self._pending_arp_set = set() self._pending_arp_set = set()
self._load_used_fip_information()
self.load_used_fip_information() def _load_used_fip_information(self):
"""Load FIP from the FipRulePriorityAllocator state file.
def load_used_fip_information(self): If, for any reason, the FIP is not stored in the state file, this
"""Some information needed to remove a floating ip e.g. it's associated method reads the namespace "ip rule" list and search for the
ip rule priorities, are stored in memory to avoid extra db lookups. corresponding fixed IP of the FIP. If present, this "ip rule" is
Since this is lost on agent restart we need to reload them. (1) deleted, (2) a new rule priority is allocated and (3) the "ip rule"
is written again with the new assigned priority.
At the end of the method, all existing "ip rule" registers in
FIP_RT_TBL table (where FIP rules are stored) that don't match with
any register memoized in self._rule_priorities is deleted.
""" """
ex_gw_port = self.get_ex_gw_port() ex_gw_port = self.get_ex_gw_port()
if ex_gw_port: if not ex_gw_port:
fip_ns = self.agent.get_fip_ns(ex_gw_port['network_id']) return
for fip in self.get_floating_ips(): fip_ns = self.agent.get_fip_ns(ex_gw_port['network_id'])
floating_ip = fip['floating_ip_address'] for fip in self.get_floating_ips():
fixed_ip = fip['fixed_ip_address'] floating_ip = fip['floating_ip_address']
rule_pr = fip_ns.lookup_rule_priority(floating_ip) fixed_ip = fip['fixed_ip_address']
if rule_pr: if not fixed_ip:
self.floating_ips_dict[floating_ip] = (fixed_ip, rule_pr) continue
else:
LOG.error("Rule priority not found for floating ip %s", rule_pr = fip_ns.lookup_rule_priority(floating_ip)
floating_ip) if rule_pr:
self.floating_ips_dict[floating_ip] = (fixed_ip, rule_pr)
continue
rule_pr = fip_ns.allocate_rule_priority(floating_ip)
ip_lib.add_ip_rule(self.ns_name, fixed_ip,
table=dvr_fip_ns.FIP_RT_TBL,
priority=rule_pr)
self.floating_ips_dict[floating_ip] = (fixed_ip, rule_pr)
self._cleanup_unused_fip_ip_rules()
def _cleanup_unused_fip_ip_rules(self):
if not self.router_namespace.exists():
# It could be a new router, thus the namespace is not created yet.
return
ip_rules = ip_lib.list_ip_rules(self.ns_name,
lib_constants.IP_VERSION_4)
ip_rules = [ipr for ipr in ip_rules
if ipr['table'] == dvr_fip_ns.FIP_RT_TBL]
for ip_rule in ip_rules:
for fixed_ip, rule_pr in self.floating_ips_dict.values():
if (ip_rule['from'] == fixed_ip and
ip_rule['priority'] == rule_pr):
break
else:
ip_lib.delete_ip_rule(self.ns_name, ip_rule['from'],
table=dvr_fip_ns.FIP_RT_TBL,
priority=ip_rule['priority'])
def migrate_centralized_floating_ip(self, fip, interface_name, device): def migrate_centralized_floating_ip(self, fip, interface_name, device):
# Remove the centralized fip first and then add fip to the host # Remove the centralized fip first and then add fip to the host
@ -182,10 +218,10 @@ class DvrLocalRouter(dvr_router_base.DvrRouterBase):
priority=int(str(rule_pr))) priority=int(str(rule_pr)))
self.fip_ns.deallocate_rule_priority(floating_ip) self.fip_ns.deallocate_rule_priority(floating_ip)
else: else:
LOG.error("Unable to find necessary information to complete " LOG.error('Floating IP %s not stored in this agent. Because of '
"removal of floating ip rules for %s - will require " 'the initilization method "_load_used_fip_information", '
"manual cleanup (see LP 1891673 for details).", 'all floating IPs should be memoized in the local '
floating_ip) 'memory.', floating_ip)
def floating_ip_removed_dist(self, fip_cidr): def floating_ip_removed_dist(self, fip_cidr):
"""Remove floating IP from FIP namespace.""" """Remove floating IP from FIP namespace."""

View File

@ -31,6 +31,9 @@ class FipPriority(object):
else: else:
return False return False
def __int__(self):
return int(self.index)
class FipRulePriorityAllocator(ItemAllocator): class FipRulePriorityAllocator(ItemAllocator):
"""Manages allocation of floating ips rule priorities. """Manages allocation of floating ips rule priorities.

View File

@ -27,6 +27,7 @@ import testtools
from neutron.agent.common import ovs_lib from neutron.agent.common import ovs_lib
from neutron.agent.l3 import agent as neutron_l3_agent from neutron.agent.l3 import agent as neutron_l3_agent
from neutron.agent.l3 import dvr_local_router
from neutron.agent.l3 import namespaces from neutron.agent.l3 import namespaces
from neutron.agent.l3 import router_info as l3_router_info from neutron.agent.l3 import router_info as l3_router_info
from neutron.agent import l3_agent as l3_agent_main from neutron.agent import l3_agent as l3_agent_main
@ -405,7 +406,9 @@ class L3AgentTestFramework(base.BaseSudoTestCase):
ovsbr.clear_db_attribute('Port', device_name, 'tag') ovsbr.clear_db_attribute('Port', device_name, 'tag')
with mock.patch(OVS_INTERFACE_DRIVER + '.plug_new', autospec=True) as ( with mock.patch(OVS_INTERFACE_DRIVER + '.plug_new', autospec=True) as (
ovs_plug): ovs_plug), \
mock.patch.object(dvr_local_router.DvrLocalRouter,
'_load_used_fip_information'):
ovs_plug.side_effect = new_ovs_plug ovs_plug.side_effect = new_ovs_plug
agent._process_added_router(router) agent._process_added_router(router)

View File

@ -215,6 +215,9 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
def setUp(self): def setUp(self):
super(TestBasicRouterOperations, self).setUp() super(TestBasicRouterOperations, self).setUp()
self.useFixture(IptablesFixture()) self.useFixture(IptablesFixture())
self._mock_load_fip = mock.patch.object(
dvr_local_router.DvrLocalRouter, '_load_used_fip_information')
self.mock_load_fip = self._mock_load_fip.start()
def test_request_id_changes(self): def test_request_id_changes(self):
a = l3_agent.L3NATAgent(HOSTNAME, self.conf) a = l3_agent.L3NATAgent(HOSTNAME, self.conf)
@ -1214,11 +1217,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
self.assertIsNone(res_ip) self.assertIsNone(res_ip)
self.assertTrue(log_error.called) self.assertTrue(log_error.called)
@mock.patch.object(dvr_router.DvrEdgeRouter, 'load_used_fip_information')
@mock.patch.object(dvr_router_base.LOG, 'error') @mock.patch.object(dvr_router_base.LOG, 'error')
def test_get_snat_port_for_internal_port_ipv6_same_port(self, def test_get_snat_port_for_internal_port_ipv6_same_port(self, log_error):
log_error,
load_used_fips):
router = l3_test_common.prepare_router_data( router = l3_test_common.prepare_router_data(
ip_version=lib_constants.IP_VERSION_4, enable_snat=True, ip_version=lib_constants.IP_VERSION_4, enable_snat=True,
num_internal_ports=1) num_internal_ports=1)

View File

@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import copy
import mock import mock
from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import portbindings
from neutron_lib import constants as lib_constants from neutron_lib import constants as lib_constants
@ -22,6 +24,7 @@ from oslo_utils import uuidutils
from neutron.agent.l3 import agent as l3_agent from neutron.agent.l3 import agent as l3_agent
from neutron.agent.l3 import dvr_edge_ha_router as dvr_edge_ha_rtr from neutron.agent.l3 import dvr_edge_ha_router as dvr_edge_ha_rtr
from neutron.agent.l3 import dvr_edge_router as dvr_edge_rtr from neutron.agent.l3 import dvr_edge_router as dvr_edge_rtr
from neutron.agent.l3 import dvr_fip_ns
from neutron.agent.l3 import dvr_local_router as dvr_router from neutron.agent.l3 import dvr_local_router as dvr_router
from neutron.agent.l3 import link_local_allocator as lla from neutron.agent.l3 import link_local_allocator as lla
from neutron.agent.l3 import router_info from neutron.agent.l3 import router_info
@ -38,6 +41,9 @@ from neutron.tests.common import l3_test_common
_uuid = uuidutils.generate_uuid _uuid = uuidutils.generate_uuid
FIP_PRI = 32768 FIP_PRI = 32768
HOSTNAME = 'myhost' HOSTNAME = 'myhost'
FIP_RULE_PRIO_LIST = [['fip_1', 'fixed_ip_1', 'prio_1'],
['fip_2', 'fixed_ip_2', 'prio_2'],
['fip_3', 'fixed_ip_3', 'prio_3']]
class TestDvrRouterOperations(base.BaseTestCase): class TestDvrRouterOperations(base.BaseTestCase):
@ -116,6 +122,10 @@ class TestDvrRouterOperations(base.BaseTestCase):
'oslo_service.loopingcall.FixedIntervalLoopingCall') 'oslo_service.loopingcall.FixedIntervalLoopingCall')
self.looping_call_p.start() self.looping_call_p.start()
self.mock_load_fip_p = mock.patch.object(dvr_router.DvrLocalRouter,
'_load_used_fip_information')
self.mock_load_fip = self.mock_load_fip_p.start()
subnet_id_1 = _uuid() subnet_id_1 = _uuid()
subnet_id_2 = _uuid() subnet_id_2 = _uuid()
self.snat_ports = [{'subnets': [{'cidr': '152.2.0.0/16', self.snat_ports = [{'subnets': [{'cidr': '152.2.0.0/16',
@ -154,9 +164,7 @@ class TestDvrRouterOperations(base.BaseTestCase):
kwargs['router'] = router kwargs['router'] = router
kwargs['agent_conf'] = self.conf kwargs['agent_conf'] = self.conf
kwargs['interface_driver'] = mock.Mock() kwargs['interface_driver'] = mock.Mock()
with mock.patch.object(dvr_router.DvrLocalRouter, return dvr_router.DvrLocalRouter(HOSTNAME, **kwargs)
'load_used_fip_information'):
return dvr_router.DvrLocalRouter(HOSTNAME, **kwargs)
def _set_ri_kwargs(self, agent, router_id, router): def _set_ri_kwargs(self, agent, router_id, router):
self.ri_kwargs['agent'] = agent self.ri_kwargs['agent'] = agent
@ -224,32 +232,40 @@ class TestDvrRouterOperations(base.BaseTestCase):
self.assertTrue( self.assertTrue(
ri.fip_ns.create_rtr_2_fip_link.called) ri.fip_ns.create_rtr_2_fip_link.called)
def test_load_used_fip_information(self): @mock.patch.object(ip_lib, 'add_ip_rule')
router = mock.MagicMock() def test__load_used_fip_information(self, mock_add_ip_rule):
with mock.patch.object(dvr_router.DvrLocalRouter, # This test will simulate how "DvrLocalRouter" reloads the FIP
'get_floating_ips') as mock_get_floating_ips: # information from both the FipNamespace._rule_priorities state file
with mock.patch.object(dvr_router.DvrLocalRouter, # and the namespace "ip rule" list.
'get_ex_gw_port') as mock_ext_port: router = self._create_router()
mock_ext_port.return_value = {'network_id': _uuid()} self.mock_load_fip_p.stop()
fip = {'id': _uuid(), fip_ns = router.agent.get_fip_ns('net_id')
'host': HOSTNAME,
'floating_ip_address': '15.1.2.3', # To simulate a partially populated FipNamespace._rule_priorities
'fixed_ip_address': '192.168.0.1', # state file, we load all FIPs but last.
'floating_network_id': _uuid(), fip_rule_prio_list = copy.deepcopy(FIP_RULE_PRIO_LIST)
'port_id': _uuid()} for idx, (fip, _, _) in enumerate(FIP_RULE_PRIO_LIST[:-1]):
fip_ns = mock.MagicMock() prio = fip_ns.allocate_rule_priority(fip)
fip_ns.lookup_rule_priority.return_value = 1234 fip_rule_prio_list[idx][2] = prio
mock_get_floating_ips.return_value = [fip]
mock_agent = mock.MagicMock() fips = [{'floating_ip_address': fip, 'fixed_ip_address': fixed_ip} for
mock_agent.get_fip_ns.return_value = fip_ns fip, fixed_ip, _ in fip_rule_prio_list]
kwargs = {'agent': mock_agent, with mock.patch.object(dvr_fip_ns.FipNamespace,
'router_id': _uuid(), 'allocate_rule_priority',
'router': mock.Mock(), return_value=fip_rule_prio_list[-1][2]), \
'agent_conf': self.conf, mock.patch.object(router, '_cleanup_unused_fip_ip_rules'), \
'interface_driver': mock.Mock()} mock.patch.object(router, 'get_floating_ips',
router = dvr_router.DvrLocalRouter(HOSTNAME, **kwargs) return_value=fips):
self.assertEqual({'15.1.2.3': ('192.168.0.1', 1234)}, router._load_used_fip_information()
router.floating_ips_dict)
mock_add_ip_rule.assert_called_once_with(
router.ns_name, fip_rule_prio_list[2][1],
table=dvr_fip_ns.FIP_RT_TBL, priority=fip_rule_prio_list[-1][2])
self.assertEqual(3, len(router.floating_ips_dict))
ret = [[fip, fixed_ip, prio] for fip, (fixed_ip, prio) in
router.floating_ips_dict.items()]
self.assertEqual(sorted(ret, key=lambda ret: ret[0]),
fip_rule_prio_list)
def test_get_floating_ips_dvr(self): def test_get_floating_ips_dvr(self):
router = mock.MagicMock() router = mock.MagicMock()

View File

@ -47,8 +47,8 @@ class TestPrefixDelegation(tests_base.DietTestCase):
self.assertEqual(ns_name, pd_router.get('ns_name')) self.assertEqual(ns_name, pd_router.get('ns_name'))
@mock.patch.object(dvr_edge_router.DvrEdgeRouter, @mock.patch.object(dvr_edge_router.DvrEdgeRouter,
'load_used_fip_information') '_load_used_fip_information')
def test_add_update_dvr_edge_router(self, load_used_fip_info): def test_add_update_dvr_edge_router(self, *args):
l3_agent = mock.Mock() l3_agent = mock.Mock()
l3_agent.pd.routers = {} l3_agent.pd.routers = {}
router_id = '1' router_id = '1'
@ -62,8 +62,8 @@ class TestPrefixDelegation(tests_base.DietTestCase):
self._test_add_update_pd(l3_agent, ri, ns_name) self._test_add_update_pd(l3_agent, ri, ns_name)
@mock.patch.object(dvr_local_router.DvrLocalRouter, @mock.patch.object(dvr_local_router.DvrLocalRouter,
'load_used_fip_information') '_load_used_fip_information')
def test_add_update_dvr_local_router(self, load_used_fip_info): def test_add_update_dvr_local_router(self, *args):
l3_agent = mock.Mock() l3_agent = mock.Mock()
l3_agent.pd.routers = {} l3_agent.pd.routers = {}
router_id = '1' router_id = '1'