Enable neutron-dynamic-routing scheduler

This executes the dr scheduler on the callback for create_bgp_speaker.
Also add the following variable for devstack.
  BGP_SCHEDULER_DRIVER: specify dr scheduler driver

Change-Id: Ibf94cc2183783f9bdb2dd245ad28d304ca930589
This commit is contained in:
fumihiko kakuma 2017-02-01 10:05:10 +09:00
parent d59cc092d2
commit 086384c3b1
8 changed files with 197 additions and 1 deletions

View File

@ -29,6 +29,12 @@ function configure_dr_agent_bgp_driver {
iniset $DR_AGENT_BGP_CONF_FILE bgp bgp_speaker_driver $BGP_SPEAKER_DRIVER
}
function configure_dr_agent_scheduler_driver {
if [ -n "$BGP_SCHEDULER_DRIVER" ] ; then
iniset $NEUTRON_CONF DEFAULT bgp_drscheduler_driver $BGP_SCHEDULER_DRIVER
fi
}
#############################
# Stack Install Section #
#############################
@ -61,6 +67,7 @@ function dr_post_configure {
if is_protocol_enabled BGP; then
configure_dr_agent_bgp_config
configure_dr_agent_bgp_driver
configure_dr_agent_scheduler_driver
fi
fi
}

View File

@ -0,0 +1,13 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
BGP_SPEAKER = 'bgp_speaker'

View File

@ -75,7 +75,7 @@ class BgpDrAgentSchedulerDbMixin(bgp_dras_ext.BgpDrSchedulerPluginBase,
def schedule_bgp_speaker(self, context, created_bgp_speaker):
if self.bgp_drscheduler:
agents = self.bgp_drscheduler.schedule(context,
agents = self.bgp_drscheduler.schedule(self, context,
created_bgp_speaker)
for agent in agents:
self._bgp_rpc.bgp_speaker_created(context,

View File

@ -27,6 +27,7 @@ from neutron.callbacks import resources
from neutron.common import rpc as n_rpc
from neutron_dynamic_routing.api.rpc.agentnotifiers import bgp_dr_rpc_agent_api # noqa
from neutron_dynamic_routing.api.rpc.callbacks import resources as dr_resources
from neutron_dynamic_routing.api.rpc.handlers import bgp_speaker_rpc as bs_rpc
from neutron_dynamic_routing.db import bgp_db
from neutron_dynamic_routing.db import bgp_dragentscheduler_db
@ -115,6 +116,10 @@ class BgpPlugin(service_base.ServicePluginBase,
def create_bgp_speaker(self, context, bgp_speaker):
bgp_speaker = super(BgpPlugin, self).create_bgp_speaker(context,
bgp_speaker)
payload = {'plugin': self, 'context': context,
'bgp_speaker': bgp_speaker}
registry.notify(dr_resources.BGP_SPEAKER, events.AFTER_CREATE,
self, payload=payload)
return bgp_speaker
def update_bgp_speaker(self, context, bgp_speaker_id, bgp_speaker):

View File

@ -13,17 +13,21 @@
# License for the specific language governing permissions and limitations
# under the License.
from neutron_lib import context as nl_context
from oslo_db import exception as db_exc
from oslo_log import log as logging
from sqlalchemy.orm import exc
from sqlalchemy import sql
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.db import agents_db
from neutron.db.models import agent as agent_model
from neutron.scheduler import base_resource_filter
from neutron.scheduler import base_scheduler
from neutron_dynamic_routing._i18n import _LI, _LW
from neutron_dynamic_routing.api.rpc.callbacks import resources as dr_resources
from neutron_dynamic_routing.db import bgp_db
from neutron_dynamic_routing.db import bgp_dragentscheduler_db as bgp_dras_db
from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts
@ -123,6 +127,11 @@ class BgpDrAgentFilter(base_resource_filter.BaseResourceFilter):
class BgpDrAgentSchedulerBase(BgpDrAgentFilter):
def _register_callbacks(self):
registry.subscribe(self.schedule_bgp_speaker_callback,
dr_resources.BGP_SPEAKER,
events.AFTER_CREATE)
def schedule_unscheduled_bgp_speakers(self, context, host):
"""Schedule unscheduled BgpSpeaker to a BgpDrAgent.
"""
@ -178,12 +187,19 @@ class BgpDrAgentSchedulerBase(BgpDrAgentFilter):
no_agent_binding)
return [bgp_speaker_id_[0] for bgp_speaker_id_ in query]
def schedule_bgp_speaker_callback(self, resource, event, trigger, payload):
plugin = payload['plugin']
if event == events.AFTER_CREATE:
ctx = nl_context.get_admin_context()
plugin.schedule_bgp_speaker(ctx, payload['bgp_speaker'])
class ChanceScheduler(base_scheduler.BaseChanceScheduler,
BgpDrAgentSchedulerBase):
def __init__(self):
super(ChanceScheduler, self).__init__(self)
self._register_callbacks()
class WeightScheduler(base_scheduler.BaseWeightScheduler,
@ -191,3 +207,4 @@ class WeightScheduler(base_scheduler.BaseWeightScheduler,
def __init__(self):
super(WeightScheduler, self).__init__(self)
self._register_callbacks()

View File

@ -13,15 +13,20 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
import testscenarios
from neutron_lib import context
from oslo_utils import importutils
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.tests.unit import testlib_api
from neutron_dynamic_routing.api.rpc.callbacks import resources as dr_resources
from neutron_dynamic_routing.db import bgp_db
from neutron_dynamic_routing.db import bgp_dragentscheduler_db as bgp_dras_db
from neutron_dynamic_routing.services.bgp import bgp_plugin
from neutron_dynamic_routing.services.bgp.scheduler import bgp_dragent_scheduler as bgp_dras # noqa
from neutron_dynamic_routing.tests.common import helpers
@ -72,6 +77,67 @@ class TestBgpDrAgentSchedulerBaseTestCase(testlib_api.SqlTestCase):
self.assertEqual(bgp_speaker_id, result.bgp_speaker_id)
class TestSchedulerCallback(TestBgpDrAgentSchedulerBaseTestCase):
def setUp(self):
super(TestSchedulerCallback, self).setUp()
bgp_notify_p = mock.patch('neutron_dynamic_routing.api.rpc.'
'agentnotifiers.bgp_dr_rpc_agent_api.'
'BgpDrAgentNotifyApi')
bgp_notify_p.start()
rpc_conn_p = mock.patch('neutron.common.rpc.create_connection')
rpc_conn_p.start()
admin_ctx_p = mock.patch('neutron_lib.context.get_admin_context')
admin_ctx_p = mock.patch('neutron_lib.context.get_admin_context')
self.admin_ctx_m = admin_ctx_p.start()
self.admin_ctx_m.return_value = self.ctx
self.plugin = bgp_plugin.BgpPlugin()
self.scheduler = bgp_dras.ChanceScheduler()
def _create_test_payload(self, context='test_ctx'):
bgp_speaker = {'id': '11111111-2222-3333-4444-555555555555'}
payload = {'plugin': self.plugin, 'context': context,
'bgp_speaker': bgp_speaker}
return payload
def test__register_callbacks(self):
with mock.patch.object(registry, 'subscribe') as subscribe:
scheduler = bgp_dras.ChanceScheduler()
expected_calls = [
mock.call(scheduler.schedule_bgp_speaker_callback,
dr_resources.BGP_SPEAKER, events.AFTER_CREATE),
]
self.assertEqual(subscribe.call_args_list, expected_calls)
with mock.patch.object(registry, 'subscribe') as subscribe:
scheduler = bgp_dras.WeightScheduler()
expected_calls = [
mock.call(scheduler.schedule_bgp_speaker_callback,
dr_resources.BGP_SPEAKER, events.AFTER_CREATE),
]
self.assertEqual(subscribe.call_args_list, expected_calls)
def test_schedule_bgp_speaker_callback_with_valid_event(self):
payload = self._create_test_payload()
with mock.patch.object(self.plugin,
'schedule_bgp_speaker') as sched_bgp:
self.scheduler.schedule_bgp_speaker_callback(
dr_resources.BGP_SPEAKER,
events.AFTER_CREATE,
self.scheduler, payload)
sched_bgp.assert_called_once_with(self.ctx,
payload['bgp_speaker'])
def test_schedule_bgp_speaker_callback_with_invalid_event(self):
payload = self._create_test_payload()
with mock.patch.object(self.plugin,
'schedule_bgp_speaker') as sched_bgp:
self.scheduler.schedule_bgp_speaker_callback(
dr_resources.BGP_SPEAKER,
events.BEFORE_CREATE,
self.scheduler, payload)
sched_bgp.assert_not_called()
class TestBgpDrAgentScheduler(TestBgpDrAgentSchedulerBaseTestCase,
bgp_db.BgpDbMixin):

View File

@ -0,0 +1,88 @@
# Copyright (C) 2017 VA Linux Systems Japan K.K.
# Copyright (C) 2017 Fumihiko Kakuma <kakuma at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
from neutron.tests import base
from neutron_dynamic_routing.api.rpc.callbacks import resources as dr_resources
from neutron_dynamic_routing.db import bgp_db
from neutron_dynamic_routing.services.bgp import bgp_plugin
class TestBgpPlugin(base.BaseTestCase):
def setUp(self):
super(TestBgpPlugin, self).setUp()
bgp_notify_p = mock.patch('neutron_dynamic_routing.api.rpc.'
'agentnotifiers.bgp_dr_rpc_agent_api.'
'BgpDrAgentNotifyApi')
bgp_notify_p.start()
rpc_conn_p = mock.patch('neutron.common.rpc.create_connection')
rpc_conn_p.start()
admin_ctx_p = mock.patch('neutron_lib.context.get_admin_context')
self.admin_ctx_m = admin_ctx_p.start()
self.fake_admin_ctx = mock.Mock()
self.admin_ctx_m.return_value = self.fake_admin_ctx
self.plugin = bgp_plugin.BgpPlugin()
def _create_test_payload(self, context='test_ctx'):
bgp_speaker = {'id': '11111111-2222-3333-4444-555555555555'}
payload = {'plugin': self.plugin, 'context': context,
'bgp_speaker': bgp_speaker}
return payload
def test__register_callbacks(self):
with mock.patch.object(registry, 'subscribe') as subscribe:
plugin = bgp_plugin.BgpPlugin()
expected_calls = [
mock.call(plugin.bgp_drscheduler.schedule_bgp_speaker_callback,
dr_resources.BGP_SPEAKER, events.AFTER_CREATE),
mock.call(plugin.floatingip_update_callback,
resources.FLOATING_IP, events.AFTER_UPDATE),
mock.call(plugin.router_interface_callback,
resources.ROUTER_INTERFACE, events.AFTER_CREATE),
mock.call(plugin.router_interface_callback,
resources.ROUTER_INTERFACE, events.BEFORE_CREATE),
mock.call(plugin.router_interface_callback,
resources.ROUTER_INTERFACE, events.AFTER_DELETE),
mock.call(plugin.router_gateway_callback,
resources.ROUTER_GATEWAY, events.AFTER_CREATE),
mock.call(plugin.router_gateway_callback,
resources.ROUTER_GATEWAY, events.AFTER_DELETE),
]
self.assertEqual(subscribe.call_args_list, expected_calls)
def test_create_bgp_speaker(self):
test_context = 'create_bgp_context'
test_bgp_speaker = {'id': None}
payload = self._create_test_payload(context=test_context)
with mock.patch.object(bgp_db.BgpDbMixin,
'create_bgp_speaker') as create_bgp_sp:
with mock.patch.object(registry, 'notify') as notify:
create_bgp_sp.return_value = payload['bgp_speaker']
self.assertEqual(self.plugin.create_bgp_speaker(
test_context, test_bgp_speaker),
payload['bgp_speaker'])
create_bgp_sp.assert_called_once_with(test_context,
test_bgp_speaker)
notify.assert_called_once_with(dr_resources.BGP_SPEAKER,
events.AFTER_CREATE,
self.plugin,
payload=payload)