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
# driver is agent-based, and whether the host is running the agent
# associated to the driver itself.
host_agent_types = utils.get_agent_types_by_host(context, binding_host)
drivers = [
driver for driver in trunk_plugin.registered_drivers
if utils.is_driver_compatible(
context, driver, vif_type, binding_host)
context, driver, vif_type, host_agent_types)
]
if len(drivers) > 1:
raise trunk_exc.TrunkPluginDriverConflict()

View File

@@ -24,18 +24,19 @@ def gen_trunk_br_name(trunk_id):
[:constants.DEVICE_NAME_MAX_LEN - 1])
def are_agent_types_available_on_host(context, agent_types, host):
"""Return true if agent types are present on the host."""
def get_agent_types_by_host(context, host):
"""Return the agent types registered on the host."""
agent_types = []
core_plugin = manager.NeutronManager.get_plugin()
if utils.is_extension_supported(core_plugin, 'agent'):
return bool(core_plugin.get_agents(
context.elevated(),
filters={'host': [host], 'agent_type': agent_types}))
return False
agents = core_plugin.get_agents(
context.elevated(), filters={'host': [host]})
agent_types = [a['agent_type'] for a in agents]
return agent_types
def is_driver_compatible(context, driver, interface, binding_host):
"""Return true if the driver is compatible with the interface and host.
def is_driver_compatible(context, driver, interface, host_agent_types):
"""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
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
# For an agent-based driver, both interface and agent compat is required.
return (
is_interface_compatible and
are_agent_types_available_on_host(
context, [driver.agent_type], binding_host))
return is_interface_compatible and driver.agent_type in host_agent_types

View File

@@ -20,50 +20,49 @@ from neutron.tests.unit.services.trunk import fakes
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(
utils.are_agent_types_available_on_host(
self.context, ['foo_type'], 'foo_host'))
utils.get_agent_types_by_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:
f.return_value = ['foo_agent']
self.assertTrue(
utils.are_agent_types_available_on_host(
self.context, ['foo_type'], 'foo_host'))
f.return_value = [{'agent_type': 'foo_type'}]
self.assertEqual(
['foo_type'],
utils.get_agent_types_by_host(
self.context, 'foo_host'))
def _test_is_driver_compatible(self, driver, interface, host, agents=None):
with mock.patch("neutron.db.agents_db.AgentDbMixin.get_agents") as f:
f.return_value = agents or []
return utils.is_driver_compatible(self.context,
driver,
interface,
host)
def _test_is_driver_compatible(self, driver, interface, agent_types):
return utils.is_driver_compatible(self.context,
driver,
interface,
agent_types)
def test_is_driver_compatible(self):
driver = fakes.FakeDriverWithAgent.create()
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):
driver = fakes.FakeDriverWithAgent.create()
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):
driver = fakes.FakeDriverWithAgent.create()
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):
driver = fakes.FakeDriver.create()
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):
driver1 = fakes.FakeDriverWithAgent.create()
driver2 = fakes.FakeDriver2.create()
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(
driver2, 'foo_intfs', 'foo_host', [{'agent_type': 'foo_type'}]))
driver2, 'foo_intfs', ['foo_type']))