Merge "Only schedule routers from drivers that need it"
This commit is contained in:
commit
dc6508aae2
@ -215,6 +215,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."""
|
||||
@ -237,6 +239,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
|
||||
@ -205,6 +206,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(
|
||||
@ -400,6 +416,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(
|
||||
@ -1655,6 +1679,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…
x
Reference in New Issue
Block a user