Only schedule routers from drivers that need it
This adjusts the L3 scheduler framework to ask the L3 plugin if a given router should be scheduled before scheduling it. To maintain backwards compatibility, this new method is implemented to return True in the base class. The L3 plugin with flavor support overrides this to lookup the driver associated with a router and check if the driver requires the L3 scheduling framework. This will allows the coexistence of flavors that needs scheduling and flavors that don't. Change-Id: I17a64c59eaf5d8605ff8ec2a29e491673be960e7 Implements: blueprint multi-l3-backends
This commit is contained in:
parent
5f849165a6
commit
b5fe13afa8
|
@ -204,6 +204,9 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
|
|||
|
||||
def add_router_to_l3_agent(self, context, agent_id, router_id):
|
||||
"""Add a l3 agent to host a router."""
|
||||
if not self.router_supports_scheduling(context, router_id):
|
||||
raise l3agentscheduler.RouterDoesntSupportScheduling(
|
||||
router_id=router_id)
|
||||
with context.session.begin(subtransactions=True):
|
||||
router = self.get_router(context, router_id)
|
||||
agent = self._get_agent(context, agent_id)
|
||||
|
|
|
@ -348,6 +348,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
|
|||
# implementation of l3 services
|
||||
return
|
||||
|
||||
if not l3_plugin.router_supports_scheduling(context, router_id):
|
||||
return
|
||||
cur_agents = l3_plugin.list_l3_agents_hosting_router(
|
||||
context, router_id)['agents']
|
||||
for agent in cur_agents:
|
||||
|
|
|
@ -184,6 +184,10 @@ class DVRL3CannotRemoveFromDvrAgent(exceptions.Conflict):
|
|||
"an agent in 'dvr' mode.")
|
||||
|
||||
|
||||
class RouterDoesntSupportScheduling(exceptions.Conflict):
|
||||
message = _("Router %(router_id)s does not support agent scheduling.")
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class L3AgentSchedulerPluginBase(object):
|
||||
"""REST API to operate the l3 agent scheduler.
|
||||
|
@ -207,6 +211,10 @@ class L3AgentSchedulerPluginBase(object):
|
|||
def list_l3_agents_hosting_router(self, context, router_id):
|
||||
pass
|
||||
|
||||
def router_supports_scheduling(self, context, router_id):
|
||||
"""Override this method to conditionally schedule routers."""
|
||||
return True
|
||||
|
||||
|
||||
def notify(context, action, router_id, agent_id):
|
||||
info = {'id': agent_id, 'router_id': router_id}
|
||||
|
|
|
@ -108,9 +108,11 @@ class L3Scheduler(object):
|
|||
filters = {'id': router_ids,
|
||||
'status': [constants.ROUTER_STATUS_ACTIVE]}
|
||||
routers = plugin.get_routers(context, filters=filters)
|
||||
return self._filter_unscheduled_routers(context, plugin, routers)
|
||||
result = self._filter_unscheduled_routers(context, plugin, routers)
|
||||
else:
|
||||
return self._get_unscheduled_routers(context, plugin)
|
||||
result = self._get_unscheduled_routers(context, plugin)
|
||||
return [r for r in result
|
||||
if plugin.router_supports_scheduling(context, r['id'])]
|
||||
|
||||
def _get_routers_can_schedule(self, context, plugin, routers, l3_agent):
|
||||
"""Get the subset of routers that can be scheduled on the L3 agent."""
|
||||
|
@ -223,6 +225,8 @@ class L3Scheduler(object):
|
|||
|
||||
def _schedule_router(self, plugin, context, router_id,
|
||||
candidates=None):
|
||||
if not plugin.router_supports_scheduling(context, router_id):
|
||||
return
|
||||
sync_router = plugin.get_router(context, router_id)
|
||||
candidates = candidates or self._get_candidates(
|
||||
plugin, context, sync_router)
|
||||
|
|
|
@ -103,6 +103,9 @@ class L3RouterPlugin(service_base.ServicePluginBase,
|
|||
" between (L2) Neutron networks and access to external"
|
||||
" networks via a NAT gateway.")
|
||||
|
||||
def router_supports_scheduling(self, context, router_id):
|
||||
return self.l3_driver_controller.uses_scheduler(context, router_id)
|
||||
|
||||
def create_floatingip(self, context, floatingip):
|
||||
"""Create floating IP.
|
||||
|
||||
|
|
|
@ -46,10 +46,15 @@ class L3ServiceProvider(object):
|
|||
The 'ha' and 'distributed' attributes below are used to determine if a
|
||||
router request with the 'ha' or 'distributed' attribute can be supported
|
||||
by this particular driver. These attributes must be present.
|
||||
|
||||
The 'use_integrated_agent_scheduler' flag indicates whether or not routers
|
||||
which belong to the driver should be automatically scheduled using the L3
|
||||
agent scheduler integrated into Neutron.
|
||||
"""
|
||||
|
||||
ha_support = UNSUPPORTED
|
||||
distributed_support = UNSUPPORTED
|
||||
use_integrated_agent_scheduler = False
|
||||
|
||||
def __init__(self, l3plugin):
|
||||
self.l3plugin = l3plugin
|
||||
|
|
|
@ -185,6 +185,11 @@ class DriverController(object):
|
|||
"distributed=%(d)s and ha=%(h)s") % {'d': distributed, 'h': ha}
|
||||
)
|
||||
|
||||
def uses_scheduler(self, context, router_id):
|
||||
"""Returns True if the integrated L3 scheduler should be used."""
|
||||
return (self._get_provider_for_router(context, router_id).
|
||||
use_integrated_agent_scheduler)
|
||||
|
||||
|
||||
class _LegacyPlusProviderConfiguration(
|
||||
provider_configuration.ProviderConfiguration):
|
||||
|
|
|
@ -17,3 +17,4 @@ from neutron.services.l3_router.service_providers import base
|
|||
|
||||
class DvrDriver(base.L3ServiceProvider):
|
||||
distributed_support = base.MANDATORY
|
||||
use_integrated_agent_scheduler = True
|
||||
|
|
|
@ -17,3 +17,4 @@ from neutron.services.l3_router.service_providers import base
|
|||
|
||||
class HaDriver(base.L3ServiceProvider):
|
||||
ha_support = base.MANDATORY
|
||||
use_integrated_agent_scheduler = True
|
||||
|
|
|
@ -17,3 +17,4 @@ from neutron.services.l3_router.service_providers import base
|
|||
|
||||
class SingleNodeDriver(base.L3ServiceProvider):
|
||||
"""Provider for single L3 agent routers."""
|
||||
use_integrated_agent_scheduler = True
|
||||
|
|
|
@ -3454,6 +3454,23 @@ class L3NatDBIntAgentSchedulingTestCase(L3BaseForIntTests,
|
|||
s2['subnet']['network_id'])
|
||||
self._assert_router_on_agent(r['router']['id'], 'host2')
|
||||
|
||||
def test_router_update_gateway_scheduling_not_supported(self):
|
||||
plugin = manager.NeutronManager.get_service_plugins().get(
|
||||
service_constants.L3_ROUTER_NAT)
|
||||
mock.patch.object(plugin, 'router_supports_scheduling',
|
||||
return_value=False).start()
|
||||
with self.router() as r:
|
||||
with self.subnet() as s1:
|
||||
with self.subnet() as s2:
|
||||
self._set_net_external(s1['subnet']['network_id'])
|
||||
self._set_net_external(s2['subnet']['network_id'])
|
||||
# this should pass even though there are multiple
|
||||
# external networks since no scheduling decision needs
|
||||
# to be made
|
||||
self._add_external_gateway_to_router(
|
||||
r['router']['id'],
|
||||
s1['subnet']['network_id'])
|
||||
|
||||
def test_router_update_gateway_no_eligible_l3_agent(self):
|
||||
with self.router() as r:
|
||||
with self.subnet() as s1:
|
||||
|
|
|
@ -25,6 +25,7 @@ from oslo_utils import importutils
|
|||
from oslo_utils import timeutils
|
||||
from sqlalchemy import orm
|
||||
import testscenarios
|
||||
import testtools
|
||||
|
||||
from neutron import context as n_context
|
||||
from neutron.db import agents_db
|
||||
|
@ -203,6 +204,21 @@ class L3SchedulerBaseTestCase(base.BaseTestCase):
|
|||
mock_get.assert_called_once_with(mock.ANY, self.plugin)
|
||||
self.assertEqual(expected_routers, unscheduled_routers)
|
||||
|
||||
def test__get_routers_to_schedule_excludes_unsupported(self):
|
||||
routers = [
|
||||
{'id': 'router_1'}, {'id': 'router_2'}, {'id': 'router_3'}
|
||||
]
|
||||
expected_routers = [{'id': 'router_2'}]
|
||||
# exclude everything except for 2
|
||||
self.plugin.router_supports_scheduling = lambda c, rid: rid[-1] == '2'
|
||||
with mock.patch.object(self.scheduler,
|
||||
'_get_unscheduled_routers') as mock_get:
|
||||
mock_get.return_value = routers
|
||||
unscheduled_routers = self.scheduler._get_routers_to_schedule(
|
||||
mock.ANY, self.plugin)
|
||||
mock_get.assert_called_once_with(mock.ANY, self.plugin)
|
||||
self.assertEqual(expected_routers, unscheduled_routers)
|
||||
|
||||
def _test__get_routers_can_schedule(self, routers, agent, target_routers):
|
||||
self.plugin.get_l3_agent_candidates.return_value = agent
|
||||
result = self.scheduler._get_routers_can_schedule(
|
||||
|
@ -398,6 +414,14 @@ class L3SchedulerTestBaseMixin(object):
|
|||
self.adminContext, agent_id,
|
||||
router['router']['id'])
|
||||
|
||||
def test__schedule_router_skips_unschedulable_routers(self):
|
||||
mock.patch.object(self.plugin, 'router_supports_scheduling',
|
||||
return_value=False).start()
|
||||
scheduler = l3_agent_scheduler.ChanceScheduler()
|
||||
self.assertIsNone(scheduler._schedule_router(self.plugin,
|
||||
self.adminContext,
|
||||
'router_id'))
|
||||
|
||||
def test_add_router_to_l3_agent_mismatch_error_dvr_to_legacy(self):
|
||||
self._register_l3_agents()
|
||||
self._prepare_l3_agent_dvr_move_exceptions(
|
||||
|
@ -1573,6 +1597,15 @@ class L3AgentSchedulerDbMixinTestCase(L3HATestCaseMixin):
|
|||
self.assertEqual({'agents': []},
|
||||
self.plugin._get_agents_dict_for_router([]))
|
||||
|
||||
def test_router_doesnt_support_scheduling(self):
|
||||
with mock.patch.object(self.plugin, 'router_supports_scheduling',
|
||||
return_value=False):
|
||||
agent = helpers.register_l3_agent(host='myhost_3')
|
||||
with testtools.ExpectedException(
|
||||
l3agent.RouterDoesntSupportScheduling):
|
||||
self.plugin.add_router_to_l3_agent(
|
||||
self.adminContext, agent.id, 'router_id')
|
||||
|
||||
def test_manual_add_ha_router_to_agent(self):
|
||||
cfg.CONF.set_override('max_l3_agents_per_router', 2)
|
||||
router, agents = self._setup_ha_router()
|
||||
|
|
|
@ -42,6 +42,16 @@ class TestDriverController(testlib_api.SqlTestCase):
|
|||
self.dc._flavor_plugin_ref.get_flavor_next_provider.return_value = [
|
||||
provider]
|
||||
|
||||
def test_uses_scheduler(self):
|
||||
self._return_provider_for_flavor('dvrha')
|
||||
router_db = mock.Mock()
|
||||
router = dict(id='router_id', flavor_id='abc123')
|
||||
self.dc._set_router_provider('router', 'PRECOMMIT_CREATE', self,
|
||||
self.ctx, router, router_db)
|
||||
self.assertTrue(self.dc.uses_scheduler(self.ctx, 'router_id'))
|
||||
self.dc.drivers['dvrha'].use_integrated_agent_scheduler = False
|
||||
self.assertFalse(self.dc.uses_scheduler(self.ctx, 'router_id'))
|
||||
|
||||
def test__set_router_provider_flavor_specified(self):
|
||||
self._return_provider_for_flavor('dvrha')
|
||||
router_db = mock.Mock()
|
||||
|
|
Loading…
Reference in New Issue