Remove fdb entries for ha router interfaces when going DOWN
When HA router's interface on host is going DOWN but router is still available on this host, L2 population mechanism driver will now send to other hosts info to remove fdb unicast entries to this port on host. It will not send FLOODING_ENTRY because this port is still on host but in standby mode and might be transformed to master in future. This solves issue with migration router from Legacy to HA. In such case, port which was originally attached to legacy router is transformed to be HA backup port before changing its status to DOWN. Now in such case unicast entries to this port and backup node will be removed properly so packets to HA router will be really send to host which is master node for router. Closes-Bug: #1785582 Change-Id: Icc14e5f5d40fc6fbb49e0f7b18cc3b15ebec8508
This commit is contained in:
parent
06f1aa6629
commit
6c300b1a9b
@ -52,6 +52,14 @@ class L2populationMechanismDriver(api.MechanismDriver):
|
||||
ip_address=ip['ip_address'])
|
||||
for ip in port['fixed_ips']]
|
||||
|
||||
def _remove_flooding(self, fdb_entries):
|
||||
for network_fdb in fdb_entries.values():
|
||||
for agent_fdb in network_fdb.get('ports', {}).values():
|
||||
try:
|
||||
agent_fdb.remove(const.FLOODING_ENTRY)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def check_vlan_transparency(self, context):
|
||||
"""L2population driver vlan transparency support."""
|
||||
return True
|
||||
@ -255,11 +263,15 @@ class L2populationMechanismDriver(api.MechanismDriver):
|
||||
l3plugin, "list_router_ids_on_host", None):
|
||||
admin_context = n_context.get_admin_context()
|
||||
port_context = context._plugin_context
|
||||
if l3plugin.list_router_ids_on_host(
|
||||
admin_context, agent_host, [port['device_id']]):
|
||||
return
|
||||
fdb_entries = self._get_agent_fdb(
|
||||
port_context, context.bottom_bound_segment, port, agent_host)
|
||||
port_context, context.bottom_bound_segment, port, agent_host,
|
||||
include_ha_router_ports=True)
|
||||
if (fdb_entries and
|
||||
l3plugin.list_router_ids_on_host(
|
||||
admin_context, agent_host, [port['device_id']])):
|
||||
# NOTE(slaweq): in case this is HA router, remove unicast
|
||||
# entries to this port but don't remove flood entry
|
||||
self._remove_flooding(fdb_entries)
|
||||
self.L2populationAgentNotify.remove_fdb_entries(
|
||||
self.rpc_ctx, fdb_entries)
|
||||
|
||||
@ -310,7 +322,8 @@ class L2populationMechanismDriver(api.MechanismDriver):
|
||||
self.L2populationAgentNotify.add_fdb_entries(self.rpc_ctx,
|
||||
other_fdb_entries)
|
||||
|
||||
def _get_agent_fdb(self, context, segment, port, agent_host):
|
||||
def _get_agent_fdb(self, context, segment, port, agent_host,
|
||||
include_ha_router_ports=False):
|
||||
if not agent_host:
|
||||
return
|
||||
|
||||
@ -338,9 +351,10 @@ class L2populationMechanismDriver(api.MechanismDriver):
|
||||
const.FLOODING_ENTRY)
|
||||
# Notify other agents to remove fdb rules for current port
|
||||
if (port['device_owner'] != const.DEVICE_OWNER_DVR_INTERFACE and
|
||||
(include_ha_router_ports or
|
||||
not l3_hamode_db.is_ha_router_port(context,
|
||||
port['device_owner'],
|
||||
port['device_id'])):
|
||||
port['device_id']))):
|
||||
fdb_entries = self._get_port_fdb_entries(port)
|
||||
other_fdb_entries[network_id]['ports'][agent_ip] += fdb_entries
|
||||
|
||||
|
@ -57,6 +57,7 @@ TEST_ROUTER_ID = 'router_id'
|
||||
|
||||
NOTIFIER = 'neutron.plugins.ml2.rpc.AgentNotifierApi'
|
||||
DEVICE_OWNER_COMPUTE = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake'
|
||||
DEVICE_OWNER_ROUTER_HA_INTF = constants.DEVICE_OWNER_ROUTER_HA_INTF + 'fake'
|
||||
|
||||
|
||||
class FakeL3PluginWithAgents(common_db_mixin.CommonDbMixin,
|
||||
@ -858,6 +859,45 @@ class TestL2PopulationRpcTestCase(test_plugin.Ml2PluginV2TestCase):
|
||||
self.mock_fanout.assert_called_with(
|
||||
mock.ANY, 'remove_fdb_entries', expected)
|
||||
|
||||
def test_update_port_down_ha_router_port(self):
|
||||
router = self._create_ha_router()
|
||||
directory.add_plugin(plugin_constants.L3, self.plugin)
|
||||
with self.subnet(network=self._network, enable_dhcp=False) as snet:
|
||||
subnet = snet['subnet']
|
||||
router_port = self._add_router_interface(subnet, router, HOST)
|
||||
router_port_device = 'tap' + router_port['id']
|
||||
|
||||
host_arg = {portbindings.HOST_ID: HOST_4, 'admin_state_up': True}
|
||||
with self.port(subnet=snet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port1:
|
||||
p1 = port1['port']
|
||||
device1 = 'tap' + p1['id']
|
||||
|
||||
self.callbacks.update_device_up(self.adminContext,
|
||||
agent_id=HOST,
|
||||
device=device1)
|
||||
self.mock_fanout.reset_mock()
|
||||
self.callbacks.update_device_down(self.adminContext,
|
||||
agent_id=HOST,
|
||||
device=router_port_device,
|
||||
host=HOST)
|
||||
|
||||
router_port_ips = [
|
||||
p['ip_address'] for p in router_port['fixed_ips']]
|
||||
expected = {
|
||||
router_port['network_id']: {
|
||||
'ports': {
|
||||
'20.0.0.1': [
|
||||
l2pop_rpc.PortInfo(router_port['mac_address'],
|
||||
router_port_ips[0])]},
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}
|
||||
|
||||
self.mock_fanout.assert_called_with(
|
||||
mock.ANY, 'remove_fdb_entries', expected)
|
||||
|
||||
def test_delete_port(self):
|
||||
self._register_ml2_agents()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user