Reduce DB hit when checking for trunk-able ports

We can hit the DB once to fetch all agent types for the
host rather than going back and forth for all the drivers
available in the deployment. That lets us save N-1 queries
where N is the number of drivers installed.

Change-Id: I5db0d73245b4e1c1bc46c063c0689ac3c5ea2bea
This commit is contained in:
Armando Migliaccio
2016-08-12 12:08:24 -07:00
parent 42f2ba88a5
commit b3c2a4aeae
3 changed files with 32 additions and 34 deletions

View File

@@ -105,10 +105,11 @@ class TrunkPortValidator(object):
# can be determined based on the vif type, whether or not the # can be determined based on the vif type, whether or not the
# driver is agent-based, and whether the host is running the agent # driver is agent-based, and whether the host is running the agent
# associated to the driver itself. # associated to the driver itself.
host_agent_types = utils.get_agent_types_by_host(context, binding_host)
drivers = [ drivers = [
driver for driver in trunk_plugin.registered_drivers driver for driver in trunk_plugin.registered_drivers
if utils.is_driver_compatible( if utils.is_driver_compatible(
context, driver, vif_type, binding_host) context, driver, vif_type, host_agent_types)
] ]
if len(drivers) > 1: if len(drivers) > 1:
raise trunk_exc.TrunkPluginDriverConflict() raise trunk_exc.TrunkPluginDriverConflict()

View File

@@ -24,18 +24,19 @@ def gen_trunk_br_name(trunk_id):
[:constants.DEVICE_NAME_MAX_LEN - 1]) [:constants.DEVICE_NAME_MAX_LEN - 1])
def are_agent_types_available_on_host(context, agent_types, host): def get_agent_types_by_host(context, host):
"""Return true if agent types are present on the host.""" """Return the agent types registered on the host."""
agent_types = []
core_plugin = manager.NeutronManager.get_plugin() core_plugin = manager.NeutronManager.get_plugin()
if utils.is_extension_supported(core_plugin, 'agent'): if utils.is_extension_supported(core_plugin, 'agent'):
return bool(core_plugin.get_agents( agents = core_plugin.get_agents(
context.elevated(), context.elevated(), filters={'host': [host]})
filters={'host': [host], 'agent_type': agent_types})) agent_types = [a['agent_type'] for a in agents]
return False return agent_types
def is_driver_compatible(context, driver, interface, binding_host): def is_driver_compatible(context, driver, interface, host_agent_types):
"""Return true if the driver is compatible with the interface and host. """True if the driver is compatible with interface and host_agent_types.
There may be edge cases where a stale view or the deployment may make the There may be edge cases where a stale view or the deployment may make the
following test fail to detect the right driver in charge of the bound port. following test fail to detect the right driver in charge of the bound port.
@@ -56,7 +57,4 @@ def is_driver_compatible(context, driver, interface, binding_host):
return is_interface_compatible return is_interface_compatible
# For an agent-based driver, both interface and agent compat is required. # For an agent-based driver, both interface and agent compat is required.
return ( return is_interface_compatible and driver.agent_type in host_agent_types
is_interface_compatible and
are_agent_types_available_on_host(
context, [driver.agent_type], binding_host))

View File

@@ -20,50 +20,49 @@ from neutron.tests.unit.services.trunk import fakes
class UtilsTestCase(test_plugin.Ml2PluginV2TestCase): class UtilsTestCase(test_plugin.Ml2PluginV2TestCase):
def test_are_agent_types_available_on_host_returns_false(self): def test_get_agent_types_by_host_returns_empty(self):
self.assertFalse( self.assertFalse(
utils.are_agent_types_available_on_host( utils.get_agent_types_by_host(
self.context, ['foo_type'], 'foo_host')) self.context, 'foo_host'))
def test_are_agent_types_available_on_host_returns_true(self): def test_get_agent_types_by_host_returns_agents(self):
with mock.patch("neutron.db.agents_db.AgentDbMixin.get_agents") as f: with mock.patch("neutron.db.agents_db.AgentDbMixin.get_agents") as f:
f.return_value = ['foo_agent'] f.return_value = [{'agent_type': 'foo_type'}]
self.assertTrue( self.assertEqual(
utils.are_agent_types_available_on_host( ['foo_type'],
self.context, ['foo_type'], 'foo_host')) utils.get_agent_types_by_host(
self.context, 'foo_host'))
def _test_is_driver_compatible(self, driver, interface, host, agents=None): def _test_is_driver_compatible(self, driver, interface, agent_types):
with mock.patch("neutron.db.agents_db.AgentDbMixin.get_agents") as f: return utils.is_driver_compatible(self.context,
f.return_value = agents or [] driver,
return utils.is_driver_compatible(self.context, interface,
driver, agent_types)
interface,
host)
def test_is_driver_compatible(self): def test_is_driver_compatible(self):
driver = fakes.FakeDriverWithAgent.create() driver = fakes.FakeDriverWithAgent.create()
self.assertTrue(self._test_is_driver_compatible( self.assertTrue(self._test_is_driver_compatible(
driver, 'foo_intfs', 'foo_host', [{'agent_type': 'foo_type'}])) driver, 'foo_intfs', ['foo_type']))
def test_is_driver_compatible_agent_based_agent_mismatch(self): def test_is_driver_compatible_agent_based_agent_mismatch(self):
driver = fakes.FakeDriverWithAgent.create() driver = fakes.FakeDriverWithAgent.create()
self.assertFalse(self._test_is_driver_compatible( self.assertFalse(self._test_is_driver_compatible(
driver, 'foo_intfs', 'foo_host')) driver, 'foo_intfs', ['foo_type_unknown']))
def test_is_driver_incompatible_because_of_interface_mismatch(self): def test_is_driver_incompatible_because_of_interface_mismatch(self):
driver = fakes.FakeDriverWithAgent.create() driver = fakes.FakeDriverWithAgent.create()
self.assertFalse(self._test_is_driver_compatible( self.assertFalse(self._test_is_driver_compatible(
driver, 'not_my_interface', 'foo_host')) driver, 'not_my_interface', ['foo_type']))
def test_is_driver_compatible_agentless(self): def test_is_driver_compatible_agentless(self):
driver = fakes.FakeDriver.create() driver = fakes.FakeDriver.create()
self.assertTrue(self._test_is_driver_compatible( self.assertTrue(self._test_is_driver_compatible(
driver, 'foo_intfs', 'foo_host')) driver, 'foo_intfs', ['foo_type']))
def test_is_driver_compatible_multiple_drivers(self): def test_is_driver_compatible_multiple_drivers(self):
driver1 = fakes.FakeDriverWithAgent.create() driver1 = fakes.FakeDriverWithAgent.create()
driver2 = fakes.FakeDriver2.create() driver2 = fakes.FakeDriver2.create()
self.assertTrue(self._test_is_driver_compatible( self.assertTrue(self._test_is_driver_compatible(
driver1, 'foo_intfs', 'foo_host', [{'agent_type': 'foo_type'}])) driver1, 'foo_intfs', ['foo_type']))
self.assertFalse(self._test_is_driver_compatible( self.assertFalse(self._test_is_driver_compatible(
driver2, 'foo_intfs', 'foo_host', [{'agent_type': 'foo_type'}])) driver2, 'foo_intfs', ['foo_type']))