Merge "Only schedule routers from drivers that need it"

This commit is contained in:
Jenkins 2016-09-07 19:04:04 +00:00 committed by Gerrit Code Review
commit dc6508aae2
13 changed files with 95 additions and 2 deletions

View File

@ -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)

View File

@ -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:

View File

@ -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}

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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()

View File

@ -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()