Merge "Enable neutron-dynamic-routing scheduler"
This commit is contained in:
commit
d1dc5d3712
devstack/lib
neutron_dynamic_routing
api/rpc/callbacks
db
services/bgp
tests/unit/services/bgp
@ -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
|
||||
}
|
||||
|
0
neutron_dynamic_routing/api/rpc/callbacks/__init__.py
Normal file
0
neutron_dynamic_routing/api/rpc/callbacks/__init__.py
Normal file
13
neutron_dynamic_routing/api/rpc/callbacks/resources.py
Normal file
13
neutron_dynamic_routing/api/rpc/callbacks/resources.py
Normal 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'
|
@ -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,
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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)
|
Loading…
x
Reference in New Issue
Block a user