diff --git a/neutron/plugins/ml2/drivers/l2pop/mech_driver.py b/neutron/plugins/ml2/drivers/l2pop/mech_driver.py index 4e3696386f3..f9be7a7eaf2 100644 --- a/neutron/plugins/ml2/drivers/l2pop/mech_driver.py +++ b/neutron/plugins/ml2/drivers/l2pop/mech_driver.py @@ -173,6 +173,43 @@ class L2populationMechanismDriver(api.MechanismDriver, return agent, agent_host, agent_ip, segment, fdb_entries + def _create_agent_fdb(self, session, agent, segment, network_id): + agent_fdb_entries = {network_id: + {'segment_id': segment['segmentation_id'], + 'network_type': segment['network_type'], + 'ports': {}}} + tunnel_network_ports = ( + self.get_dvr_network_ports(session, network_id).all()) + fdb_network_ports = ( + self.get_nondvr_network_ports(session, network_id).all()) + ports = agent_fdb_entries[network_id]['ports'] + ports.update(self._get_tunnels( + fdb_network_ports + tunnel_network_ports, + agent.host)) + for agent_ip, fdbs in ports.items(): + for binding, agent in fdb_network_ports: + if self.get_agent_ip(agent) == agent_ip: + fdbs.extend(self._get_port_fdb_entries(binding.port)) + + return agent_fdb_entries + + def _get_tunnels(self, tunnel_network_ports, exclude_host): + agents = {} + for _, agent in tunnel_network_ports: + if agent.host == exclude_host: + continue + + ip = self.get_agent_ip(agent) + if not ip: + LOG.debug("Unable to retrieve the agent ip, check " + "the agent %s configuration.", agent.host) + continue + + if ip not in agents: + agents[ip] = [const.FLOODING_ENTRY] + + return agents + def _update_port_up(self, context): port = context.current agent_host = context.host @@ -191,63 +228,27 @@ class L2populationMechanismDriver(api.MechanismDriver, {'segment_id': segment['segmentation_id'], 'network_type': segment['network_type'], 'ports': {agent_ip: []}}} + other_fdb_ports = other_fdb_entries[network_id]['ports'] if agent_active_ports == 1 or ( self.get_agent_uptime(agent) < cfg.CONF.l2pop.agent_boot_time): # First port activated on current agent in this network, # we have to provide it with the whole list of fdb entries - agent_fdb_entries = {network_id: - {'segment_id': segment['segmentation_id'], - 'network_type': segment['network_type'], - 'ports': {}}} - ports = agent_fdb_entries[network_id]['ports'] - - nondvr_network_ports = self.get_nondvr_network_ports(session, - network_id) - for network_port in nondvr_network_ports: - binding, agent = network_port - if agent.host == agent_host: - continue - - ip = self.get_agent_ip(agent) - if not ip: - LOG.debug("Unable to retrieve the agent ip, check " - "the agent %(agent_host)s configuration.", - {'agent_host': agent.host}) - continue - - agent_ports = ports.get(ip, [const.FLOODING_ENTRY]) - agent_ports += self._get_port_fdb_entries(binding.port) - ports[ip] = agent_ports - - dvr_network_ports = self.get_dvr_network_ports(session, network_id) - for network_port in dvr_network_ports: - binding, agent = network_port - if agent.host == agent_host: - continue - - ip = self.get_agent_ip(agent) - if not ip: - LOG.debug("Unable to retrieve the agent ip, check " - "the agent %(agent_host)s configuration.", - {'agent_host': agent.host}) - continue - - agent_ports = ports.get(ip, [const.FLOODING_ENTRY]) - ports[ip] = agent_ports + agent_fdb_entries = self._create_agent_fdb(session, + agent, + segment, + network_id) # And notify other agents to add flooding entry - other_fdb_entries[network_id]['ports'][agent_ip].append( - const.FLOODING_ENTRY) + other_fdb_ports[agent_ip].append(const.FLOODING_ENTRY) - if ports.keys(): + if agent_fdb_entries[network_id]['ports'].keys(): self.L2populationAgentNotify.add_fdb_entries( self.rpc_ctx, agent_fdb_entries, agent_host) # Notify other agents to add fdb rule for current port if port['device_owner'] != const.DEVICE_OWNER_DVR_INTERFACE: - other_fdb_entries[network_id]['ports'][agent_ip] += ( - port_fdb_entries) + other_fdb_ports[agent_ip] += port_fdb_entries self.L2populationAgentNotify.add_fdb_entries(self.rpc_ctx, other_fdb_entries) diff --git a/neutron/tests/unit/ml2/drivers/test_l2population.py b/neutron/tests/unit/ml2/drivers/test_l2population.py index 4f352a93719..0102f46d8aa 100644 --- a/neutron/tests/unit/ml2/drivers/test_l2population.py +++ b/neutron/tests/unit/ml2/drivers/test_l2population.py @@ -26,10 +26,12 @@ from neutron.db import agents_db from neutron.extensions import portbindings from neutron.extensions import providernet as pnet from neutron import manager +from neutron.plugins.ml2.drivers.l2pop import db as l2pop_db from neutron.plugins.ml2.drivers.l2pop import mech_driver as l2pop_mech_driver from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc from neutron.plugins.ml2 import managers from neutron.plugins.ml2 import rpc +from neutron.tests import base from neutron.tests.unit.ml2 import test_ml2_plugin as test_plugin HOST = 'my_l2_host' @@ -788,3 +790,104 @@ class TestL2PopulationRpcTestCase(test_plugin.Ml2PluginV2TestCase): rem_fdb_entries): l2pop_mech.delete_port_postcommit(mock.Mock()) self.assertTrue(upd_port_down.called) + + +class TestL2PopulationMechDriver(base.BaseTestCase): + + def _test_get_tunnels(self, agent_ip, exclude_host=True): + mech_driver = l2pop_mech_driver.L2populationMechanismDriver() + agent = mock.Mock() + agent.host = HOST + network_ports = ((None, agent),) + with mock.patch.object(l2pop_db.L2populationDbMixin, + 'get_agent_ip', + return_value=agent_ip): + excluded_host = HOST + '-EXCLUDE' if exclude_host else HOST + return mech_driver._get_tunnels(network_ports, excluded_host) + + def test_get_tunnels(self): + tunnels = self._test_get_tunnels('20.0.0.1') + self.assertTrue('20.0.0.1' in tunnels) + + def test_get_tunnels_no_ip(self): + tunnels = self._test_get_tunnels(None) + self.assertEqual(0, len(tunnels)) + + def test_get_tunnels_dont_exclude_host(self): + tunnels = self._test_get_tunnels(None, exclude_host=False) + self.assertEqual(0, len(tunnels)) + + def _test_create_agent_fdb(self, fdb_network_ports_query, agent_ips): + mech_driver = l2pop_mech_driver.L2populationMechanismDriver() + tunnel_network_ports_query, tunnel_agent = ( + self._mock_network_ports_query(HOST + '1', None)) + agent_ips[tunnel_agent] = '10.0.0.1' + + def agent_ip_side_effect(agent): + return agent_ips[agent] + + with contextlib.nested( + mock.patch.object(l2pop_db.L2populationDbMixin, + 'get_agent_ip', + side_effect=agent_ip_side_effect), + mock.patch.object(l2pop_db.L2populationDbMixin, + 'get_nondvr_network_ports', + new=fdb_network_ports_query), + mock.patch.object(l2pop_db.L2populationDbMixin, + 'get_dvr_network_ports', + new=tunnel_network_ports_query)): + session = mock.Mock() + agent = mock.Mock() + agent.host = HOST + segment = {'segmentation_id': 1, 'network_type': 'vxlan'} + return mech_driver._create_agent_fdb(session, + agent, + segment, + 'network_id') + + def _mock_network_ports_query(self, host_name, binding): + agent = mock.Mock() + agent.host = host_name + result = [(binding, agent)] + all_mock = mock.Mock() + all_mock.all = mock.Mock(return_value=result) + mock_query = mock.Mock(return_value=all_mock) + return mock_query, agent + + def test_create_agent_fdb(self): + binding = mock.Mock() + binding.port = {'mac_address': '00:00:DE:AD:BE:EF', + 'fixed_ips': [{'ip_address': '1.1.1.1'}]} + fdb_network_ports_query, fdb_agent = ( + self._mock_network_ports_query(HOST + '2', binding)) + agent_ips = {fdb_agent: '20.0.0.1'} + + agent_fdb = self._test_create_agent_fdb(fdb_network_ports_query, + agent_ips) + result = agent_fdb['network_id'] + + expected_result = {'segment_id': 1, + 'network_type': 'vxlan', + 'ports': + {'10.0.0.1': + [constants.FLOODING_ENTRY], + '20.0.0.1': + [constants.FLOODING_ENTRY, + l2pop_rpc.PortInfo( + mac_address='00:00:DE:AD:BE:EF', + ip_address='1.1.1.1')]}} + self.assertEqual(expected_result, result) + + def test_create_agent_fdb_only_tunnels(self): + all_mock = mock.Mock() + all_mock.all = mock.Mock(return_value=[]) + fdb_network_ports_query = mock.Mock(return_value=all_mock) + agent_fdb = self._test_create_agent_fdb(fdb_network_ports_query, {}) + result = agent_fdb['network_id'] + + expected_result = {'segment_id': 1, + 'network_type': 'vxlan', + 'ports': + {'10.0.0.1': + [constants.FLOODING_ENTRY]}} + self.assertEqual(expected_result, result)