]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Extract l2pop/DVR controller logic to common method
authorMike Kolesnik <mkolesni@redhat.com>
Mon, 8 Dec 2014 08:03:05 +0000 (10:03 +0200)
committerMike Kolesnik <mkolesni@redhat.com>
Mon, 26 Jan 2015 11:57:58 +0000 (13:57 +0200)
Regular ports and DVR ports are treated almost the same, extract the
l2pop logic to treat them to a unified method that gets an argument if
the FDB entries are needed or not, in order to reduce code duplication.

Change-Id: I1f2a1de4cb7c29724e2354577f915a48173a1966

neutron/plugins/ml2/drivers/l2pop/mech_driver.py
neutron/tests/unit/ml2/drivers/test_l2population.py

index 4e3696386f3cbaba719c1c35f7a5b3c23eed7c9d..f9be7a7eaf28e97bcd658fead0e879939f1e19d7 100644 (file)
@@ -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)
index 4f352a93719db80a810c5fdffc2f5f0f2d24b8b8..0102f46d8aa4b39b89cad3ba975ca0128897ded1 100644 (file)
@@ -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)