From e2c1bcfc94027f1428e4c697b0a43c84b4f8ed6c Mon Sep 17 00:00:00 2001 From: mathieu-rohon Date: Thu, 20 Feb 2014 10:39:43 +0100 Subject: [PATCH] l2-population : send flooding entries when the last port goes down Delete port used to call update_port_down to calculate its fdb entries during delete_port_precommit. But during the pre-commit, the port is still up, so update_port_down acts as if there was still one port on the agent, and doesn't add flooding entry in its fdb_entries. Closes bug #1282141 Change-Id: Icedd02f33d999200505950beb9169ae3634e551b --- .../plugins/ml2/drivers/l2pop/mech_driver.py | 11 +- .../unit/ml2/drivers/test_l2population.py | 146 ++++++++++++------ 2 files changed, 107 insertions(+), 50 deletions(-) diff --git a/neutron/plugins/ml2/drivers/l2pop/mech_driver.py b/neutron/plugins/ml2/drivers/l2pop/mech_driver.py index a22df7b40d..df9bcbb57a 100644 --- a/neutron/plugins/ml2/drivers/l2pop/mech_driver.py +++ b/neutron/plugins/ml2/drivers/l2pop/mech_driver.py @@ -43,7 +43,11 @@ class L2populationMechanismDriver(api.MechanismDriver, ip['ip_address']] for ip in port['fixed_ips']] def delete_port_precommit(self, context): - self.remove_fdb_entries = self._update_port_down(context) + # TODO(matrohon): revisit once the original bound segment will be + # available in delete_port_postcommit. in delete_port_postcommit + # agent_active_ports will be equal to 0, and the _update_port_down + # won't need agent_active_ports_count_for_flooding anymore + self.remove_fdb_entries = self._update_port_down(context, 1) def delete_port_postcommit(self, context): l2pop_rpc.L2populationAgentNotify.remove_fdb_entries( @@ -192,7 +196,8 @@ class L2populationMechanismDriver(api.MechanismDriver, l2pop_rpc.L2populationAgentNotify.add_fdb_entries(self.rpc_ctx, other_fdb_entries) - def _update_port_down(self, context): + def _update_port_down(self, context, + agent_active_ports_count_for_flooding=0): port_context = context.current port_infos = self._get_port_infos(context, port_context) if not port_infos: @@ -211,7 +216,7 @@ class L2populationMechanismDriver(api.MechanismDriver, 'network_type': segment['network_type'], 'ports': {agent_ip: []}}} - if agent_active_ports == 1: + if agent_active_ports == agent_active_ports_count_for_flooding: # Agent is removing its last activated port in this network, # other agents needs to be notified to delete their flooding entry. other_fdb_entries[network_id]['ports'][agent_ip].append( diff --git a/neutron/tests/unit/ml2/drivers/test_l2population.py b/neutron/tests/unit/ml2/drivers/test_l2population.py index c02cd6b277..352405ad0c 100644 --- a/neutron/tests/unit/ml2/drivers/test_l2population.py +++ b/neutron/tests/unit/ml2/drivers/test_l2population.py @@ -332,37 +332,43 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase): mock.ANY, expected2, topic=self.fanout_topic) - def test_fdb_remove_called_from_rpc(self): + def test_update_port_down(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST} with self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), - **host_arg): + **host_arg) as port1: with self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), - **host_arg) as port: - p1 = port['port'] - - device = 'tap' + p1['id'] + **host_arg) as port2: + p2 = port2['port'] + device2 = 'tap' + p2['id'] self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=HOST, - device=device) + device=device2) + 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=device) + device=device2) - p1_ips = [p['ip_address'] for p in p1['fixed_ips']] + p2_ips = [p['ip_address'] for p in p2['fixed_ips']] expected = {'args': {'fdb_entries': - {p1['network_id']: + {p2['network_id']: {'ports': - {'20.0.0.1': [[p1['mac_address'], - p1_ips[0]]]}, + {'20.0.0.1': [[p2['mac_address'], + p2_ips[0]]]}, 'network_type': 'vxlan', 'segment_id': 1}}}, 'namespace': None, @@ -371,7 +377,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase): self.mock_fanout.assert_called_with( mock.ANY, expected, topic=self.fanout_topic) - def test_fdb_remove_called(self): + def test_update_port_down_last_port_up(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: @@ -379,7 +385,87 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase): with self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), **host_arg): + with self.port(subnet=subnet, + arg_list=(portbindings.HOST_ID,), + **host_arg) as port2: + p2 = port2['port'] + device2 = 'tap' + p2['id'] + self.mock_fanout.reset_mock() + self.callbacks.update_device_up(self.adminContext, + agent_id=HOST, + device=device2) + + self.callbacks.update_device_down(self.adminContext, + agent_id=HOST, + device=device2) + + p2_ips = [p['ip_address'] for p in p2['fixed_ips']] + expected = {'args': + {'fdb_entries': + {p2['network_id']: + {'ports': + {'20.0.0.1': [constants.FLOODING_ENTRY, + [p2['mac_address'], + p2_ips[0]]]}, + 'network_type': 'vxlan', + 'segment_id': 1}}}, + 'namespace': None, + 'method': 'remove_fdb_entries'} + + self.mock_fanout.assert_called_with( + mock.ANY, expected, topic=self.fanout_topic) + + def test_delete_port(self): + self._register_ml2_agents() + + with self.subnet(network=self._network) as subnet: + host_arg = {portbindings.HOST_ID: HOST} + with self.port(subnet=subnet, + arg_list=(portbindings.HOST_ID,), + **host_arg) as port: + p1 = port['port'] + device = 'tap' + p1['id'] + + self.mock_fanout.reset_mock() + self.callbacks.update_device_up(self.adminContext, + agent_id=HOST, + device=device) + + with self.port(subnet=subnet, + arg_list=(portbindings.HOST_ID,), + **host_arg) as port2: + p2 = port2['port'] + device1 = 'tap' + p2['id'] + + self.mock_fanout.reset_mock() + self.callbacks.update_device_up(self.adminContext, + agent_id=HOST, + device=device1) + + p2_ips = [p['ip_address'] for p in p2['fixed_ips']] + expected = {'args': + {'fdb_entries': + {p2['network_id']: + {'ports': + {'20.0.0.1': [[p2['mac_address'], + p2_ips[0]]]}, + 'network_type': 'vxlan', + 'segment_id': 1}}}, + 'namespace': None, + 'method': 'remove_fdb_entries'} + + self.mock_fanout.assert_any_call( + mock.ANY, expected, topic=self.fanout_topic) + + def test_delete_port_last_port_up(self): + self._register_ml2_agents() + + with self.subnet(network=self._network) as subnet: + host_arg = {portbindings.HOST_ID: HOST} + with self.port(subnet=subnet, + arg_list=(portbindings.HOST_ID,), + **host_arg): with self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), **host_arg) as port: @@ -387,7 +473,6 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase): device = 'tap' + p1['id'] - self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device) @@ -408,39 +493,6 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase): self.mock_fanout.assert_any_call( mock.ANY, expected, topic=self.fanout_topic) - def test_fdb_remove_called_last_port(self): - self._register_ml2_agents() - - with self.subnet(network=self._network) as subnet: - host_arg = {portbindings.HOST_ID: HOST} - - with self.port(subnet=subnet, - arg_list=(portbindings.HOST_ID,), - **host_arg) as port: - p1 = port['port'] - - device = 'tap' + p1['id'] - - self.callbacks.update_device_up(self.adminContext, - agent_id=HOST, - device=device) - - p1_ips = [p['ip_address'] for p in p1['fixed_ips']] - expected = {'args': - {'fdb_entries': - {p1['network_id']: - {'ports': - {'20.0.0.1': [constants.FLOODING_ENTRY, - [p1['mac_address'], - p1_ips[0]]]}, - 'network_type': 'vxlan', - 'segment_id': 1}}}, - 'namespace': None, - 'method': 'remove_fdb_entries'} - - self.mock_fanout.assert_any_call( - mock.ANY, expected, topic=self.fanout_topic) - def test_fixed_ips_changed(self): self._register_ml2_agents()