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
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)

View 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)