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:
parent
51fcac0389
commit
86a6639294
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue