Extract l2pop/DVR controller logic to common method

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
This commit is contained in:
Mike Kolesnik 2014-12-08 10:03:05 +02:00
parent 51fcac0389
commit 86a6639294
2 changed files with 148 additions and 44 deletions

View File

@ -173,6 +173,43 @@ class L2populationMechanismDriver(api.MechanismDriver,
return agent, agent_host, agent_ip, segment, fdb_entries 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): def _update_port_up(self, context):
port = context.current port = context.current
agent_host = context.host agent_host = context.host
@ -191,63 +228,27 @@ class L2populationMechanismDriver(api.MechanismDriver,
{'segment_id': segment['segmentation_id'], {'segment_id': segment['segmentation_id'],
'network_type': segment['network_type'], 'network_type': segment['network_type'],
'ports': {agent_ip: []}}} 'ports': {agent_ip: []}}}
other_fdb_ports = other_fdb_entries[network_id]['ports']
if agent_active_ports == 1 or ( if agent_active_ports == 1 or (
self.get_agent_uptime(agent) < cfg.CONF.l2pop.agent_boot_time): self.get_agent_uptime(agent) < cfg.CONF.l2pop.agent_boot_time):
# First port activated on current agent in this network, # First port activated on current agent in this network,
# we have to provide it with the whole list of fdb entries # we have to provide it with the whole list of fdb entries
agent_fdb_entries = {network_id: agent_fdb_entries = self._create_agent_fdb(session,
{'segment_id': segment['segmentation_id'], agent,
'network_type': segment['network_type'], segment,
'ports': {}}} network_id)
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
# And notify other agents to add flooding entry # And notify other agents to add flooding entry
other_fdb_entries[network_id]['ports'][agent_ip].append( other_fdb_ports[agent_ip].append(const.FLOODING_ENTRY)
const.FLOODING_ENTRY)
if ports.keys(): if agent_fdb_entries[network_id]['ports'].keys():
self.L2populationAgentNotify.add_fdb_entries( self.L2populationAgentNotify.add_fdb_entries(
self.rpc_ctx, agent_fdb_entries, agent_host) self.rpc_ctx, agent_fdb_entries, agent_host)
# Notify other agents to add fdb rule for current port # Notify other agents to add fdb rule for current port
if port['device_owner'] != const.DEVICE_OWNER_DVR_INTERFACE: if port['device_owner'] != const.DEVICE_OWNER_DVR_INTERFACE:
other_fdb_entries[network_id]['ports'][agent_ip] += ( other_fdb_ports[agent_ip] += port_fdb_entries
port_fdb_entries)
self.L2populationAgentNotify.add_fdb_entries(self.rpc_ctx, self.L2populationAgentNotify.add_fdb_entries(self.rpc_ctx,
other_fdb_entries) other_fdb_entries)

View File

@ -26,10 +26,12 @@ from neutron.db import agents_db
from neutron.extensions import portbindings from neutron.extensions import portbindings
from neutron.extensions import providernet as pnet from neutron.extensions import providernet as pnet
from neutron import manager 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 mech_driver as l2pop_mech_driver
from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc
from neutron.plugins.ml2 import managers from neutron.plugins.ml2 import managers
from neutron.plugins.ml2 import rpc from neutron.plugins.ml2 import rpc
from neutron.tests import base
from neutron.tests.unit.ml2 import test_ml2_plugin as test_plugin from neutron.tests.unit.ml2 import test_ml2_plugin as test_plugin
HOST = 'my_l2_host' HOST = 'my_l2_host'
@ -788,3 +790,104 @@ class TestL2PopulationRpcTestCase(test_plugin.Ml2PluginV2TestCase):
rem_fdb_entries): rem_fdb_entries):
l2pop_mech.delete_port_postcommit(mock.Mock()) l2pop_mech.delete_port_postcommit(mock.Mock())
self.assertTrue(upd_port_down.called) 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)