Updating status, name field and handling reboot operation
* Status update * New unit test cases * Addition of field 'name' for association to DB and other operations. * Handle reboot cases Signed-off-by: Manu B <manu.b@est.tech> Change-Id: I81fa87752c9c6eb48990f099577f20aa48af3103
This commit is contained in:
parent
58042bb4ee
commit
b62135aa3d
|
@ -0,0 +1,119 @@
|
|||
# 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.
|
||||
|
||||
from neutron.api.rpc.callbacks import events as rpc_events
|
||||
from neutron.api.rpc.callbacks import resources as resources_registry
|
||||
from neutron.api.rpc.handlers import resources_rpc
|
||||
|
||||
from neutron_dynamic_routing.objects import bgp_associations as assoc_objects
|
||||
|
||||
|
||||
class BgpL3AgentNotifyApi(object):
|
||||
"""API for plugin to notify BGP L3Agent.
|
||||
|
||||
This class implements the client side of an rpc interface. The server side
|
||||
is neutron_dynamic_routing.services.bgp.agent.l3.bgp_extension. For
|
||||
more information about rpc interfaces, please see
|
||||
https://docs.openstack.org/neutron/latest/contributor/internals/rpc_api.html.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._register_resources()
|
||||
self.push_api = resources_rpc.ResourcesPushRpcApi()
|
||||
|
||||
def _register_resources(self):
|
||||
resources_registry.register_resource_class(
|
||||
assoc_objects.BgpSpeakerRouterAssociation)
|
||||
resources_registry.register_resource_class(
|
||||
assoc_objects.BgpSpeakerPeerAssociation)
|
||||
|
||||
def bgp_peer_association_created(self, context, bgp_speaker_id,
|
||||
assoc_info):
|
||||
"""Tell L3 agent about a BGP Peer Association.
|
||||
This effectively tells the BgpL3Agent to start a peering session.
|
||||
"""
|
||||
peer_assoc_obj = assoc_objects.BgpSpeakerPeerAssociation(
|
||||
context,
|
||||
**assoc_info)
|
||||
self.push_api.push(context, [peer_assoc_obj], rpc_events.CREATED)
|
||||
|
||||
def bgp_peer_association_deleted(self, context, bgp_speaker_id,
|
||||
assoc_info):
|
||||
"""Tell L3 agent about a BGP Peer disassociation.
|
||||
This effectively tells the BgpL3Agent to stop a peering session.
|
||||
"""
|
||||
peer_assoc_obj = assoc_objects.BgpSpeakerPeerAssociation(context,
|
||||
**assoc_info)
|
||||
self.push_api.push(context, [peer_assoc_obj], rpc_events.DELETED)
|
||||
|
||||
def bgp_router_association_created(self, context, bgp_speaker_id,
|
||||
assoc_info):
|
||||
"""Tell L3 agent about a BGP Router Association.
|
||||
This effectively tells the BgpL3Agent to start a speaker inside
|
||||
router namespace.
|
||||
"""
|
||||
router_assoc_obj = assoc_objects.BgpSpeakerRouterAssociation(
|
||||
context,
|
||||
**assoc_info)
|
||||
self.push_api.push(context, [router_assoc_obj], rpc_events.CREATED)
|
||||
|
||||
def bgp_router_association_updated(self, context, bgp_speaker_id,
|
||||
assoc_info):
|
||||
"""Tell L3 agent about a BGP Router Association updation."""
|
||||
router_assoc_obj = assoc_objects.BgpSpeakerRouterAssociation(
|
||||
context,
|
||||
**assoc_info)
|
||||
self.push_api.push(context, [router_assoc_obj], rpc_events.UPDATED)
|
||||
|
||||
def bgp_router_association_deleted(self, context, bgp_speaker_id,
|
||||
assoc_info):
|
||||
"""Tell L3 agent about a BGP Router disassociation.
|
||||
This effectively tells the BgpL3Agent to stop the speaker inside
|
||||
router namespace.
|
||||
"""
|
||||
router_assoc_obj = assoc_objects.BgpSpeakerRouterAssociation(
|
||||
context,
|
||||
**assoc_info)
|
||||
self.push_api.push(context, [router_assoc_obj], rpc_events.DELETED)
|
||||
|
||||
def agent_updated(self, context, admin_state_up, host):
|
||||
pass
|
||||
|
||||
def bgp_routes_advertisement(self, context, bgp_speaker_id,
|
||||
routes, host):
|
||||
"""Currently this is handled by L3 agent itself on router and peer
|
||||
association.
|
||||
This can be used in future to support FIP and other scenarios for
|
||||
router attached speakers.
|
||||
"""
|
||||
|
||||
def bgp_routes_withdrawal(self, context, bgp_speaker_id,
|
||||
routes, host):
|
||||
pass
|
||||
|
||||
def bgp_speaker_created(self, context, bgp_speaker_id, host):
|
||||
"""BGP speaker is currently created whenever router is associated
|
||||
to BGP speaker.
|
||||
"""
|
||||
|
||||
def bgp_speaker_removed(self, context, bgp_speaker_id, host):
|
||||
pass
|
||||
|
||||
def bgp_peer_disassociated(self, context, bgp_speaker_id,
|
||||
bgp_peer_ip, host=None):
|
||||
"""Currently router attached speakers only support peer association
|
||||
objects. But this can be enhanced in future
|
||||
"""
|
||||
|
||||
def bgp_peer_associated(self, context, bgp_speaker_id,
|
||||
bgp_peer_id, host=None):
|
||||
pass
|
|
@ -83,3 +83,15 @@ class BgpSpeakerRpcCallback(object):
|
|||
"""Returns the list of BGP speakers to which the router is associated
|
||||
"""
|
||||
return self.plugin.get_speaker_associated_to_router(context, router_id)
|
||||
|
||||
def update_speaker_router_association_status(self, context, assoc_id,
|
||||
status):
|
||||
"""Updates the status of router association"""
|
||||
self.plugin.update_speaker_router_association_status(context,
|
||||
assoc_id, status)
|
||||
|
||||
def update_speaker_peer_association_status(self, context, assoc_id,
|
||||
status):
|
||||
"""Updates the status of peer association"""
|
||||
self.plugin.update_speaker_peer_association_status(context, assoc_id,
|
||||
status)
|
||||
|
|
|
@ -21,6 +21,8 @@ from neutron.db.models import address_scope as address_scope_db
|
|||
from neutron.db.models import l3 as l3_db
|
||||
from neutron.db.models import l3_attrs as l3_attrs_db
|
||||
from neutron.db import models_v2
|
||||
from neutron.objects import agent as ag_obj
|
||||
from neutron.objects import l3agent as rb_obj
|
||||
from neutron.objects import ports
|
||||
from neutron.objects import subnet as subnet_obj
|
||||
from neutron.objects import subnetpool as subnetpool_obj
|
||||
|
@ -81,6 +83,7 @@ class BgpSpeakerRouterAssociation(model_base.BASEV2,
|
|||
|
||||
__tablename__ = 'bgp_speaker_router_associations'
|
||||
|
||||
name = sa.Column(sa.String(255), nullable=False)
|
||||
bgp_speaker_id = sa.Column(sa.String(length=36),
|
||||
sa.ForeignKey('bgp_speakers.id'),
|
||||
nullable=False)
|
||||
|
@ -99,6 +102,7 @@ class BgpSpeakerPeerAssociation(model_base.BASEV2,
|
|||
|
||||
__tablename__ = 'bgp_speaker_peer_associations'
|
||||
|
||||
name = sa.Column(sa.String(255), nullable=False)
|
||||
bgp_speaker_id = sa.Column(sa.String(length=36),
|
||||
sa.ForeignKey('bgp_speakers.id'),
|
||||
nullable=False)
|
||||
|
@ -323,7 +327,7 @@ class BgpDbMixin(object):
|
|||
except sa_exc.NoResultFound:
|
||||
raise l3_exc.RouterNotFound(router_id=router_id)
|
||||
|
||||
res_keys = ['bgp_speaker_id', 'tenant_id', 'router_id',
|
||||
res_keys = ['name', 'bgp_speaker_id', 'tenant_id', 'router_id',
|
||||
'advertise_extra_routes', 'status']
|
||||
res = dict((k, assoc_info[k]) for k in res_keys)
|
||||
self._validate_router_association(context, bgp_speaker_id,
|
||||
|
@ -344,6 +348,13 @@ class BgpDbMixin(object):
|
|||
return self._make_bgp_speaker_router_association_dict(
|
||||
speaker_router_assoc_db)
|
||||
|
||||
def update_speaker_router_association_status(self, context, id, status):
|
||||
assoc_info = {}
|
||||
assoc_info['status'] = status
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
assoc_db = self._get_bgp_speaker_router_association(context, id)
|
||||
assoc_db.update(assoc_info)
|
||||
|
||||
def delete_bgp_speaker_router_association(self, context, id):
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
speaker_router_assoc_db = \
|
||||
|
@ -388,7 +399,8 @@ class BgpDbMixin(object):
|
|||
except sa_exc.NoResultFound:
|
||||
raise bgp_ext.BgpPeerNotFound(id=peer_id)
|
||||
|
||||
res_keys = ['bgp_speaker_id', 'tenant_id', 'peer_id', 'status']
|
||||
res_keys = ['name', 'bgp_speaker_id', 'tenant_id', 'peer_id',
|
||||
'status']
|
||||
res = dict((k, assoc_info[k]) for k in res_keys)
|
||||
self._validate_peer_association(context, bgp_speaker_id, peer)
|
||||
res['id'] = uuidutils.generate_uuid()
|
||||
|
@ -397,6 +409,13 @@ class BgpDbMixin(object):
|
|||
return self._make_bgp_speaker_peer_association_dict(
|
||||
speaker_peer_assoc_db)
|
||||
|
||||
def update_speaker_peer_association_status(self, context, id, status):
|
||||
assoc_info = {}
|
||||
assoc_info['status'] = status
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
assoc_db = self._get_bgp_speaker_peer_association(context, id)
|
||||
assoc_db.update(assoc_info)
|
||||
|
||||
def delete_bgp_speaker_peer_association(self, context, id):
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
assoc_db = self._get_bgp_speaker_peer_association(context, id)
|
||||
|
@ -506,8 +525,13 @@ class BgpDbMixin(object):
|
|||
return self._make_advertised_routes_dict(routes)
|
||||
|
||||
def get_routes(self, context, bgp_speaker_id):
|
||||
"""TODO: Add implementation to list advertised and learnt routes"""
|
||||
pass
|
||||
assocs = self._get_bgp_speaker_router_association_by_speaker_id(
|
||||
context, bgp_speaker_id)
|
||||
routes = []
|
||||
for assoc in assocs:
|
||||
routes.extend(self._get_routes_by_router_association(
|
||||
context, assoc['router_id'], bgp_speaker_id))
|
||||
return {'routes': self._prefix_list_from_route_tuples(routes)}
|
||||
|
||||
def get_speaker_associated_to_router(self, context, router_id):
|
||||
return self._get_bgp_speaker_router_association_by_router_id(context,
|
||||
|
@ -551,6 +575,7 @@ class BgpDbMixin(object):
|
|||
models_v2.SubnetPool.address_scope_id == address_scope.id,
|
||||
BgpSpeaker.id == bgp_speaker_id,
|
||||
BgpSpeaker.ip_version == address_scope.ip_version,
|
||||
BgpSpeaker.advertise_tenant_networks == sa.sql.true(),
|
||||
models_v2.Subnet.ip_version == address_scope.ip_version)
|
||||
return tenant_router_query.all()
|
||||
|
||||
|
@ -785,6 +810,16 @@ class BgpDbMixin(object):
|
|||
raise bgp_asso_ext.DuplicateBgpSpeakerPeerAssociation(
|
||||
bgp_speaker_id=speaker_id,
|
||||
peer_id=new_peer.id)
|
||||
# Do not allow peer association if network is already associated to
|
||||
# BGP speaker. Currently association objects are only supported with
|
||||
# router attached BGP speakers.
|
||||
network_bindings = self._get_bgp_speaker_network_binding_by_speaker_id(
|
||||
context,
|
||||
speaker_id)
|
||||
if len(network_bindings) == 1:
|
||||
raise bgp_asso_ext.InvalidBgpSpeakerAssociation(
|
||||
bgp_speaker_id=speaker_id,
|
||||
network_id=network_bindings[0]['network_id'])
|
||||
|
||||
# Do not allow peer association if network is already associated to
|
||||
# BGP speaker. Currently association objects are only supported with
|
||||
|
@ -806,14 +841,15 @@ class BgpDbMixin(object):
|
|||
|
||||
def _make_bgp_speaker_router_association_dict(self, router_association,
|
||||
fields=None):
|
||||
attrs = ['id', 'tenant_id', 'router_id', 'bgp_speaker_id',
|
||||
attrs = ['id', 'name', 'tenant_id', 'router_id', 'bgp_speaker_id',
|
||||
'advertise_extra_routes', 'status']
|
||||
res = dict((k, router_association[k]) for k in attrs)
|
||||
return db_utils.resource_fields(res, fields)
|
||||
|
||||
def _make_bgp_speaker_peer_association_dict(self, peer_association,
|
||||
fields=None):
|
||||
attrs = ['id', 'tenant_id', 'peer_id', 'bgp_speaker_id', 'status']
|
||||
attrs = ['id', 'name', 'tenant_id', 'peer_id', 'bgp_speaker_id',
|
||||
'status']
|
||||
res = dict((k, peer_association[k]) for k in attrs)
|
||||
return db_utils.resource_fields(res, fields)
|
||||
|
||||
|
@ -1570,3 +1606,13 @@ class BgpDbMixin(object):
|
|||
ext_nets.append(net)
|
||||
|
||||
return ext_nets
|
||||
|
||||
def get_l3_agent_hosting_router(self, context, router_id):
|
||||
with db_api.CONTEXT_READER.using(context):
|
||||
record_objs = rb_obj.RouterL3AgentBinding.get_objects(
|
||||
context, router_id=[router_id])
|
||||
l3_agents = [
|
||||
ag_obj.Agent.get_object(context, id=obj.l3_agent_id)
|
||||
for obj in record_objs
|
||||
]
|
||||
return l3_agents[0]
|
||||
|
|
|
@ -274,3 +274,69 @@ class BgpDrAgentSchedulerDbMixin(bgp_dras_ext.BgpDrSchedulerPluginBase,
|
|||
return self.get_bgp_peer(context, bgp_peer_id)
|
||||
except exc.NoResultFound:
|
||||
return {}
|
||||
|
||||
def _create_or_update_bgp_speaker_l3agent_binding(self, context, agent_id,
|
||||
speaker_id,
|
||||
dragent_id=None):
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
agent_db = self._get_agent(context, agent_id)
|
||||
is_agent_l3 = (agent_db['agent_type'] ==
|
||||
bgp_consts.L3_AGENT_BGP_ROUTING)
|
||||
if not is_agent_l3:
|
||||
raise bgp_dras_ext.DrAgentInvalid(id=agent_id)
|
||||
|
||||
binding = BgpSpeakerDrAgentBinding()
|
||||
binding.bgp_speaker_id = speaker_id
|
||||
binding.agent_id = agent_id
|
||||
query = context.session.query(BgpSpeakerDrAgentBinding)
|
||||
if dragent_id:
|
||||
try:
|
||||
existing_binding = query.filter_by(
|
||||
bgp_speaker_id=speaker_id, agent_id=dragent_id).one()
|
||||
existing_binding.update(binding)
|
||||
except exc.NoResultFound:
|
||||
#Create a new binding with l3agent and speaker mapping
|
||||
context.session.add(binding)
|
||||
else:
|
||||
context.session.add(binding)
|
||||
|
||||
def reschedule_bgp_speaker_on_l3_agent(self, context, bgp_speaker_id,
|
||||
l3_agent_id):
|
||||
dragents = self.get_dragents_hosting_bgp_speakers(
|
||||
context, [bgp_speaker_id])
|
||||
if len(dragents) > 1:
|
||||
for dragent in dragents[1:]:
|
||||
self.remove_bgp_speaker_from_dragent(context, dragent.id,
|
||||
bgp_speaker_id)
|
||||
|
||||
#Replacing dragent with l3agent so that it is not autoscheduled
|
||||
if dragents:
|
||||
dragent_id = dragents[0].id
|
||||
self._create_or_update_bgp_speaker_l3agent_binding(
|
||||
context, l3_agent_id, bgp_speaker_id, dragent_id)
|
||||
agent_db = self._get_agent(context, dragent_id)
|
||||
self._bgp_rpc.bgp_speaker_removed(context, bgp_speaker_id,
|
||||
agent_db.host)
|
||||
else:
|
||||
self._create_or_update_bgp_speaker_l3agent_binding(
|
||||
context, l3_agent_id, bgp_speaker_id)
|
||||
|
||||
def remove_bgp_speaker_from_l3agent(self, context, bgp_speaker_id):
|
||||
l3agent = self.get_dragents_hosting_bgp_speakers(
|
||||
context, [bgp_speaker_id])[0]
|
||||
agent_id = l3agent.id
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
agent_db = self._get_agent(context, agent_id)
|
||||
is_agent_l3 = (agent_db['agent_type'] ==
|
||||
bgp_consts.L3_AGENT_BGP_ROUTING)
|
||||
if not is_agent_l3:
|
||||
raise bgp_dras_ext.DrAgentInvalid(id=agent_id)
|
||||
|
||||
query = context.session.query(BgpSpeakerDrAgentBinding)
|
||||
query = query.filter_by(bgp_speaker_id=bgp_speaker_id,
|
||||
agent_id=agent_id)
|
||||
query.delete()
|
||||
LOG.debug('BgpSpeaker %(bgp_speaker_id)s removed from '
|
||||
'L3Agent %(agent_id)s',
|
||||
{'bgp_speaker_id': bgp_speaker_id,
|
||||
'agent_id': agent_id})
|
||||
|
|
|
@ -33,6 +33,7 @@ def upgrade():
|
|||
'bgp_speaker_router_associations',
|
||||
sa.Column('id', sa.String(length=constants.UUID_FIELD_SIZE),
|
||||
nullable=False),
|
||||
sa.Column('name', sa.String(255), nullable=False),
|
||||
sa.Column('project_id', sa.String(length=255)),
|
||||
sa.Column('bgp_speaker_id', sa.String(length=36),
|
||||
sa.ForeignKey('bgp_speakers.id'), nullable=False),
|
||||
|
@ -45,6 +46,7 @@ def upgrade():
|
|||
'bgp_speaker_peer_associations',
|
||||
sa.Column('id', sa.String(length=constants.UUID_FIELD_SIZE),
|
||||
nullable=False),
|
||||
sa.Column('name', sa.String(255), nullable=False),
|
||||
sa.Column('project_id', sa.String(length=255)),
|
||||
sa.Column('bgp_speaker_id', sa.String(length=36),
|
||||
sa.ForeignKey('bgp_speakers.id'),
|
||||
|
|
|
@ -73,7 +73,7 @@ class DuplicateBgpSpeakerRouterAssociation(n_exc.Conflict):
|
|||
"router with id %(router_id)s.")
|
||||
|
||||
|
||||
class InvalidBgpSpeakerRouterAssociation(n_exc.Conflict):
|
||||
class InvalidBgpSpeakerAssociation(n_exc.Conflict):
|
||||
message = _("BGP Speaker %(bgp_speaker_id)s is already associated to "
|
||||
"network with id %(network_id)s.")
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ from os_ken.services.protocols.bgp import net_ctrl
|
|||
from os_ken.services.protocols.bgp.rtconf import neighbors
|
||||
from os_ken.services.protocols.bgp.rtconf.neighbors import PASSWORD
|
||||
from oslo_log import log as logging
|
||||
import tenacity
|
||||
|
||||
from neutron.privileged.agent.linux import ip_lib
|
||||
from neutron_dynamic_routing._i18n import _LI
|
||||
|
@ -75,6 +76,9 @@ def del_bgp_speaker(bgp_speaker_id, ns):
|
|||
|
||||
|
||||
@privileged.bgp_speaker_cmd.entrypoint
|
||||
@tenacity.retry(retry=tenacity.retry_if_exception_type(
|
||||
(EOFError, ConnectionRefusedError)),
|
||||
wait=tenacity.wait_random(min=1, max=2))
|
||||
def add_bgp_neighbor(bgp_speaker_id, peer_ip, remote_as, ns,
|
||||
password=None, auth_type='none'):
|
||||
with open(get_netns_path(ns)) as fd:
|
||||
|
|
|
@ -129,6 +129,8 @@ class BgpAgentExtension(l3_extension.L3AgentExtension):
|
|||
context, assoc.bgp_speaker_id, assoc.router_id)
|
||||
self.routes_cache.create_speaker_routes(assoc.bgp_speaker_id,
|
||||
routes)
|
||||
self.rpc_plugin.update_speaker_router_association_status(
|
||||
context, assoc.id, bgp_consts.STATUS_ACTIVE)
|
||||
elif event_type == rpc_events.UPDATED:
|
||||
if assoc.advertise_extra_routes:
|
||||
routes = self.rpc_plugin.get_routes_to_advertise(
|
||||
|
@ -168,6 +170,8 @@ class BgpAgentExtension(l3_extension.L3AgentExtension):
|
|||
priv_driver.advertise_routes(
|
||||
tuple(self.routes_cache.get_speaker_routes(
|
||||
peer_assoc.bgp_speaker_id)))
|
||||
self.rpc_plugin.update_speaker_peer_association_status(
|
||||
context, peer_assoc.id, bgp_consts.STATUS_ACTIVE)
|
||||
elif event_type == rpc_events.DELETED:
|
||||
priv_driver.del_bgp_neighbor(peer_assoc.bgp_speaker_id,
|
||||
peer['peer_ip'],
|
||||
|
@ -181,7 +185,33 @@ class BgpAgentExtension(l3_extension.L3AgentExtension):
|
|||
:param context: RPC context.
|
||||
:param data: Router data.
|
||||
"""
|
||||
pass
|
||||
LOG.debug("ADD ROUTER %s", data)
|
||||
router_info = self.agent_api.get_router_info(data['id'])
|
||||
if router_info:
|
||||
router_assocs = self.rpc_plugin.get_speaker_associated_to_router(
|
||||
context, data['id'])
|
||||
for router_assoc in router_assocs:
|
||||
bgp_speaker_id = router_assoc['bgp_speaker_id']
|
||||
sp = self.rpc_plugin.get_bgp_speaker_info(context,
|
||||
bgp_speaker_id)
|
||||
if priv_driver.PROCESS_CACHE.get_bgp_speaker_process(
|
||||
bgp_speaker_id):
|
||||
LOG.debug("BGP process already running for speaker %s",
|
||||
bgp_speaker_id)
|
||||
continue
|
||||
priv_driver.add_bgp_speaker(bgp_speaker_id,
|
||||
sp['local_as'], '1.1.1.1',
|
||||
router_info.ns_name,
|
||||
sp['ip_version'])
|
||||
LOG.debug("ADD ROUTER PEER ASSOC %s", sp['peer_associations'])
|
||||
for peer_assoc in sp['peer_associations']:
|
||||
peer = self.rpc_plugin.get_bgp_peer_info(
|
||||
context, peer_assoc['peer_id'])
|
||||
LOG.debug("ADD ROUTER PEER %s", peer)
|
||||
priv_driver.add_bgp_neighbor(peer_assoc['bgp_speaker_id'],
|
||||
peer['peer_ip'],
|
||||
peer['remote_as'],
|
||||
router_info.ns_name)
|
||||
|
||||
def update_router(self, context, data):
|
||||
"""Handle a router update event.
|
||||
|
|
|
@ -65,3 +65,17 @@ class BgpL3ExtPluginApi(object):
|
|||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'get_speaker_associated_to_router',
|
||||
router_id=router_id)
|
||||
|
||||
def update_speaker_router_association_status(self, context, assoc_id,
|
||||
status):
|
||||
"""Make a remote process call to update router association status"""
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'update_speaker_router_association_status',
|
||||
assoc_id=assoc_id, status=status)
|
||||
|
||||
def update_speaker_peer_association_status(self, context, assoc_id,
|
||||
status):
|
||||
"""Make a remote process call to update peer association status"""
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'update_speaker_peer_association_status',
|
||||
assoc_id=assoc_id, status=status)
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
from netaddr import IPAddress
|
||||
|
||||
from neutron.api.rpc.callbacks import events as rpc_events
|
||||
from neutron.api.rpc.callbacks import resources as resources_registry
|
||||
from neutron.api.rpc.handlers import resources_rpc
|
||||
from neutron_lib.api.definitions import bgp as bgp_ext
|
||||
|
@ -34,10 +33,12 @@ from oslo_log import log as logging
|
|||
from oslo_utils import importutils
|
||||
|
||||
from neutron_dynamic_routing.api.rpc.agentnotifiers import bgp_dr_rpc_agent_api # noqa
|
||||
from neutron_dynamic_routing.api.rpc.agentnotifiers import l3_bgp_rpc_agent_api
|
||||
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
|
||||
from neutron_dynamic_routing.extensions import bgp_dragentscheduler as bgp_dras_ext # noqa
|
||||
from neutron_dynamic_routing.objects import bgp_associations as assoc_objects
|
||||
from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts
|
||||
|
||||
|
@ -153,10 +154,11 @@ class BgpPlugin(service_base.ServicePluginBase,
|
|||
context,
|
||||
[bgp_speaker_id])
|
||||
super(BgpPlugin, self).delete_bgp_speaker(context, bgp_speaker_id)
|
||||
agent_rpc = self.get_rpc(context, bgp_speaker_id)
|
||||
for agent in hosted_bgp_dragents:
|
||||
self._bgp_rpc.bgp_speaker_removed(context,
|
||||
bgp_speaker_id,
|
||||
agent.host)
|
||||
agent_rpc.bgp_speaker_removed(context,
|
||||
bgp_speaker_id,
|
||||
agent.host)
|
||||
|
||||
def get_bgp_peers(self, context, fields=None, filters=None, sorts=None,
|
||||
limit=None, marker=None, page_reverse=False):
|
||||
|
@ -189,10 +191,11 @@ class BgpPlugin(service_base.ServicePluginBase,
|
|||
hosted_bgp_dragents = self.get_dragents_hosting_bgp_speakers(
|
||||
context,
|
||||
[bgp_speaker_id])
|
||||
agent_rpc = self.get_rpc(context, bgp_speaker_id)
|
||||
for agent in hosted_bgp_dragents:
|
||||
self._bgp_rpc.bgp_peer_associated(context, bgp_speaker_id,
|
||||
ret_value['bgp_peer_id'],
|
||||
agent.host)
|
||||
agent_rpc.bgp_peer_associated(context, bgp_speaker_id,
|
||||
ret_value['bgp_peer_id'],
|
||||
agent.host)
|
||||
return ret_value
|
||||
|
||||
def remove_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info):
|
||||
|
@ -202,12 +205,12 @@ class BgpPlugin(service_base.ServicePluginBase,
|
|||
ret_value = super(BgpPlugin, self).remove_bgp_peer(context,
|
||||
bgp_speaker_id,
|
||||
bgp_peer_info)
|
||||
|
||||
agent_rpc = self.get_rpc(context, bgp_speaker_id)
|
||||
for agent in hosted_bgp_dragents:
|
||||
self._bgp_rpc.bgp_peer_disassociated(context,
|
||||
bgp_speaker_id,
|
||||
ret_value['bgp_peer_id'],
|
||||
agent.host)
|
||||
agent_rpc.bgp_peer_disassociated(context,
|
||||
bgp_speaker_id,
|
||||
ret_value['bgp_peer_id'],
|
||||
agent.host)
|
||||
|
||||
def add_bgp_speaker_to_dragent(self, context, agent_id, speaker_id):
|
||||
super(BgpPlugin, self).add_bgp_speaker_to_dragent(context,
|
||||
|
@ -262,10 +265,16 @@ class BgpPlugin(service_base.ServicePluginBase,
|
|||
bgp_speaker_id,
|
||||
router_association)
|
||||
|
||||
router_assoc_obj = assoc_objects.BgpSpeakerRouterAssociation(
|
||||
context,
|
||||
**assoc_info)
|
||||
self.push_api.push(context, [router_assoc_obj], rpc_events.CREATED)
|
||||
#By default, BGP speaker is bound to dr agent. On router association
|
||||
#it has to be rescheduled to l3 agent
|
||||
router_id = assoc_info['router_id']
|
||||
l3_agent = super(BgpPlugin, self).get_l3_agent_hosting_router(
|
||||
context, router_id)
|
||||
super(BgpPlugin, self).reschedule_bgp_speaker_on_l3_agent(
|
||||
context, bgp_speaker_id, l3_agent.id)
|
||||
agent_rpc = self.get_rpc(context, bgp_speaker_id)
|
||||
agent_rpc.bgp_router_association_created(context, bgp_speaker_id,
|
||||
assoc_info)
|
||||
return assoc_info
|
||||
|
||||
def update_bgp_speaker_router_association(self, context, assoc_id,
|
||||
|
@ -274,21 +283,31 @@ class BgpPlugin(service_base.ServicePluginBase,
|
|||
.update_bgp_speaker_router_association(context, assoc_id,
|
||||
router_assoc)
|
||||
|
||||
router_assoc_obj = assoc_objects.BgpSpeakerRouterAssociation(
|
||||
context,
|
||||
**assoc_info)
|
||||
self.push_api.push(context, [router_assoc_obj], rpc_events.UPDATED)
|
||||
agent_rpc = self.get_rpc(context, bgp_speaker_id)
|
||||
agent_rpc.bgp_router_association_updated(context, bgp_speaker_id,
|
||||
assoc_info)
|
||||
return assoc_info
|
||||
|
||||
def update_speaker_router_association_status(self, context, assoc_id,
|
||||
status):
|
||||
super(BgpPlugin, self).update_speaker_router_association_status(
|
||||
context, assoc_id, status)
|
||||
|
||||
def delete_bgp_speaker_router_association(self, context, id,
|
||||
bgp_speaker_id):
|
||||
assoc_info = super(BgpPlugin, self)\
|
||||
.delete_bgp_speaker_router_association(context, id)
|
||||
|
||||
router_assoc_obj = assoc_objects.BgpSpeakerRouterAssociation(
|
||||
context,
|
||||
**assoc_info)
|
||||
self.push_api.push(context, [router_assoc_obj], rpc_events.DELETED)
|
||||
agent_rpc = self.get_rpc(context, bgp_speaker_id)
|
||||
agent_rpc.bgp_router_association_deleted(context, bgp_speaker_id,
|
||||
assoc_info)
|
||||
#Remove l3 agent to speaker mapping and reschedule bgp speaker
|
||||
#to BGP DR agent again
|
||||
super(BgpPlugin, self).remove_bgp_speaker_from_l3agent(context,
|
||||
bgp_speaker_id)
|
||||
|
||||
bgp_speaker = self.get_bgp_speaker(context, bgp_speaker_id)
|
||||
super(BgpPlugin, self).schedule_bgp_speaker(context, bgp_speaker)
|
||||
|
||||
def get_bgp_speaker_peer_associations(
|
||||
self, context, bgp_speaker_id,
|
||||
|
@ -314,20 +333,24 @@ class BgpPlugin(service_base.ServicePluginBase,
|
|||
.create_bgp_speaker_peer_association(context, bgp_speaker_id,
|
||||
peer_association)
|
||||
|
||||
peer_assoc_obj = assoc_objects.BgpSpeakerPeerAssociation(
|
||||
context,
|
||||
**assoc_info)
|
||||
self.push_api.push(context, [peer_assoc_obj], rpc_events.CREATED)
|
||||
agent_rpc = self.get_rpc(context, bgp_speaker_id)
|
||||
agent_rpc.bgp_peer_association_created(context, bgp_speaker_id,
|
||||
assoc_info)
|
||||
return assoc_info
|
||||
|
||||
def update_speaker_peer_association_status(self, context, assoc_id,
|
||||
status):
|
||||
super(BgpPlugin, self).update_speaker_peer_association_status(
|
||||
context, assoc_id, status)
|
||||
|
||||
def delete_bgp_speaker_peer_association(self, context, id,
|
||||
bgp_speaker_id):
|
||||
assoc_info = super(BgpPlugin, self)\
|
||||
.delete_bgp_speaker_peer_association(context, id)
|
||||
|
||||
peer_assoc_obj = assoc_objects.BgpSpeakerPeerAssociation(context,
|
||||
**assoc_info)
|
||||
self.push_api.push(context, [peer_assoc_obj], rpc_events.DELETED)
|
||||
agent_rpc = self.get_rpc(context, bgp_speaker_id)
|
||||
agent_rpc.bgp_peer_association_deleted(context, bgp_speaker_id,
|
||||
assoc_info)
|
||||
|
||||
def get_speaker_associated_to_router(self, context, router_id):
|
||||
return super(BgpPlugin, self).get_speaker_associated_to_router(
|
||||
|
@ -515,11 +538,12 @@ class BgpPlugin(service_base.ServicePluginBase,
|
|||
def start_route_advertisements(self, ctx, bgp_rpc,
|
||||
bgp_speaker_id, routes):
|
||||
agents = self.list_dragent_hosting_bgp_speaker(ctx, bgp_speaker_id)
|
||||
agent_rpc = self.get_rpc(context, bgp_speaker_id)
|
||||
for agent in agents['agents']:
|
||||
bgp_rpc.bgp_routes_advertisement(ctx,
|
||||
bgp_speaker_id,
|
||||
routes,
|
||||
agent['host'])
|
||||
agent_rpc.bgp_routes_advertisement(ctx,
|
||||
bgp_speaker_id,
|
||||
routes,
|
||||
agent['host'])
|
||||
|
||||
msg = "Starting route advertisements for %s on BgpSpeaker %s"
|
||||
self._debug_log_for_routes(msg, routes, bgp_speaker_id)
|
||||
|
@ -527,11 +551,12 @@ class BgpPlugin(service_base.ServicePluginBase,
|
|||
def stop_route_advertisements(self, ctx, bgp_rpc,
|
||||
bgp_speaker_id, routes):
|
||||
agents = self.list_dragent_hosting_bgp_speaker(ctx, bgp_speaker_id)
|
||||
agent_rpc = self.get_rpc(context, bgp_speaker_id)
|
||||
for agent in agents['agents']:
|
||||
bgp_rpc.bgp_routes_withdrawal(ctx,
|
||||
bgp_speaker_id,
|
||||
routes,
|
||||
agent['host'])
|
||||
agent_rpc.bgp_routes_withdrawal(ctx,
|
||||
bgp_speaker_id,
|
||||
routes,
|
||||
agent['host'])
|
||||
|
||||
msg = "Stopping route advertisements for %s on BgpSpeaker %s"
|
||||
self._debug_log_for_routes(msg, routes, bgp_speaker_id)
|
||||
|
@ -542,3 +567,19 @@ class BgpPlugin(service_base.ServicePluginBase,
|
|||
if LOG.isEnabledFor(logging.DEBUG):
|
||||
for route in routes:
|
||||
LOG.debug(msg, route, bgp_speaker_id)
|
||||
|
||||
def get_rpc(self, context, bgp_speaker_id):
|
||||
hosted_agents = self.get_dragents_hosting_bgp_speakers(
|
||||
context, [bgp_speaker_id])
|
||||
rpc = self._bgp_rpc
|
||||
if len(hosted_agents) >= 1:
|
||||
#Checking agent type of a single binding
|
||||
agent_id = hosted_agents[0].id
|
||||
agent_db = self._get_agent(context, agent_id)
|
||||
if agent_db['agent_type'] == bgp_consts.AGENT_TYPE_BGP_ROUTING:
|
||||
return rpc
|
||||
elif agent_db['agent_type'] == bgp_consts.L3_AGENT_BGP_ROUTING:
|
||||
rpc = l3_bgp_rpc_agent_api.BgpL3AgentNotifyApi()
|
||||
else:
|
||||
raise bgp_dras_ext.DrAgentInvalid(id=agent_id)
|
||||
return rpc
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
# under the License.
|
||||
|
||||
AGENT_TYPE_BGP_ROUTING = 'BGP dynamic routing agent'
|
||||
L3_AGENT_BGP_ROUTING = 'L3 agent'
|
||||
|
||||
BGP_DRAGENT = 'bgp_dragent'
|
||||
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
# 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.
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from neutron.agent.l3 import agent as l3_agent
|
||||
from neutron.agent.l3 import l3_agent_extension_api as l3_ext_api
|
||||
from neutron.agent.l3 import router_info as l3router
|
||||
from neutron.api.rpc.callbacks.consumer import registry
|
||||
from neutron.api.rpc.callbacks import events
|
||||
from neutron.api.rpc.handlers import resources_rpc
|
||||
from neutron.tests.unit.agent.l3 import test_agent
|
||||
from neutron_lib import constants
|
||||
from neutron_lib import context
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron_dynamic_routing.objects import bgp_associations as assoc_objects
|
||||
from neutron_dynamic_routing.privileged import driver as priv_driver
|
||||
from neutron_dynamic_routing.services.bgp.agent.l3 import bgp_extension
|
||||
|
||||
BGP_ROUTER_ASSOC = assoc_objects.BgpSpeakerRouterAssociation.obj_name()
|
||||
BGP_PEER_ASSOC = assoc_objects.BgpSpeakerPeerAssociation.obj_name()
|
||||
HOSTNAME = 'testhost'
|
||||
FAKE_BGPSPEAKER_UUID = uuidutils.generate_uuid()
|
||||
FAKE_ROUTER_ASSOC_UUID = uuidutils.generate_uuid()
|
||||
FAKE_PEER_ASSOC_UUID = uuidutils.generate_uuid()
|
||||
FAKE_ROUTER_UUID = uuidutils.generate_uuid()
|
||||
FAKE_BGPPEER_UUID = uuidutils.generate_uuid()
|
||||
FAKE_BGP_SPEAKER = {'id': FAKE_BGPSPEAKER_UUID,
|
||||
'ip_version': 4,
|
||||
'local_as': 12345,
|
||||
'peers': [{'remote_as': '2345',
|
||||
'peer_ip': '1.1.1.1',
|
||||
'auth_type': 'none',
|
||||
'password': ''}],
|
||||
'router_associations': [{
|
||||
'bgp_speaker_id': FAKE_BGPSPEAKER_UUID,
|
||||
'router_id': FAKE_ROUTER_UUID,
|
||||
'advertise_extra_routes': True}],
|
||||
'peer_associations': [{
|
||||
'bgp_speaker_id': FAKE_BGPSPEAKER_UUID,
|
||||
'peer_id': FAKE_BGPPEER_UUID}],
|
||||
'advertised_routes': []}
|
||||
FAKE_BGP_PEER = {'id': FAKE_BGPPEER_UUID,
|
||||
'remote_as': '2345',
|
||||
'peer_ip': '1.1.1.1',
|
||||
'auth_type': 'none',
|
||||
'password': ''}
|
||||
FAKE_ROUTES = ['192.168.145.0/24', '145.0.0.0/24']
|
||||
FAKE_EXTRA_ROUTE = ['145.0.0.0/24']
|
||||
|
||||
|
||||
class BgpL3AgentExtensionBaseTestCase(
|
||||
test_agent.BasicRouterOperationsFramework):
|
||||
|
||||
def setUp(self):
|
||||
super(BgpL3AgentExtensionBaseTestCase, self).setUp()
|
||||
|
||||
self.bgp_ext = bgp_extension.BgpAgentExtension()
|
||||
self.agent_api = l3_ext_api.L3AgentExtensionAPI(None, None)
|
||||
self.bgp_ext.consume_api(self.agent_api)
|
||||
self.connection = mock.Mock()
|
||||
self.context = context.get_admin_context()
|
||||
self.agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
||||
self.router_id = FAKE_ROUTER_UUID
|
||||
self.router = {'id': self.router_id,
|
||||
'ha': False,
|
||||
'distributed': False}
|
||||
self.router_info = l3router.RouterInfo(self.agent, self.router_id,
|
||||
self.router, **self.ri_kwargs)
|
||||
self.agent.router_info[self.router['id']] = self.router_info
|
||||
|
||||
self.get_router_info = mock.patch(
|
||||
'neutron.agent.l3.l3_agent_extension_api.'
|
||||
'L3AgentExtensionAPI.get_router_info').start()
|
||||
self.get_router_info.return_value = self.router_info
|
||||
|
||||
|
||||
class BgpL3AgentExtensionInitializeTestCase(BgpL3AgentExtensionBaseTestCase):
|
||||
@mock.patch.object(registry, 'register')
|
||||
@mock.patch.object(resources_rpc, 'ResourcesPushRpcCallback')
|
||||
def test_initialize_subscribed_to_rpc(self, rpc_mock, subscribe_mock):
|
||||
call_to_patch = 'neutron_lib.rpc.Connection'
|
||||
with mock.patch(call_to_patch,
|
||||
return_value=self.connection) as create_connection:
|
||||
self.bgp_ext.initialize(
|
||||
self.connection, constants.L3_AGENT_MODE)
|
||||
create_connection.assert_has_calls([mock.call()])
|
||||
self.connection.create_consumer.assert_has_calls(
|
||||
[mock.call(
|
||||
resources_rpc.resource_type_versioned_topic(
|
||||
BGP_ROUTER_ASSOC),
|
||||
[rpc_mock()],
|
||||
fanout=True),
|
||||
mock.call(
|
||||
resources_rpc.resource_type_versioned_topic(
|
||||
BGP_PEER_ASSOC),
|
||||
[rpc_mock()],
|
||||
fanout=True)]
|
||||
)
|
||||
subscribe_mock.assert_any_call(
|
||||
mock.ANY, BGP_ROUTER_ASSOC)
|
||||
subscribe_mock.assert_called_with(
|
||||
mock.ANY, BGP_PEER_ASSOC)
|
||||
|
||||
|
||||
class BGPL3AgentExtensionRouterAssocTestCase(BgpL3AgentExtensionBaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(BGPL3AgentExtensionRouterAssocTestCase, self).setUp()
|
||||
self.bgp_ext.initialize(
|
||||
self.connection, constants.L3_AGENT_MODE)
|
||||
router_assoc_info = {'id': FAKE_ROUTER_ASSOC_UUID,
|
||||
'router_id': self.router_id,
|
||||
'bgp_speaker_id': FAKE_BGPSPEAKER_UUID,
|
||||
'advertise_extra_routes': True}
|
||||
self.router_assoc = assoc_objects.BgpSpeakerRouterAssociation(
|
||||
self.context, **router_assoc_info)
|
||||
peer_assoc_info = {'id': FAKE_PEER_ASSOC_UUID,
|
||||
'peer_id': FAKE_BGPPEER_UUID,
|
||||
'bgp_speaker_id': FAKE_BGPSPEAKER_UUID}
|
||||
self.peer_assoc = assoc_objects.BgpSpeakerPeerAssociation(
|
||||
self.context, **peer_assoc_info)
|
||||
self.bgp_ext.rpc_plugin = mock.Mock()
|
||||
self.plugin_rpc = self.bgp_ext.rpc_plugin
|
||||
self.plugin_rpc.get_bgp_speaker_info.return_value = FAKE_BGP_SPEAKER
|
||||
self.plugin_rpc.get_bgp_peer_info.return_value = FAKE_BGP_PEER
|
||||
self.plugin_rpc.get_routes_to_advertise.return_value = FAKE_ROUTES
|
||||
self.plugin_rpc.get_routes_to_withdraw.return_value = FAKE_EXTRA_ROUTE
|
||||
self.plugin_rpc.get_speaker_associated_to_router.return_value = [
|
||||
router_assoc_info]
|
||||
|
||||
@mock.patch.object(priv_driver, 'add_bgp_speaker')
|
||||
def test_handle_create_router_assoc(self, driver_mock):
|
||||
self.bgp_ext._handle_router_notification(self.context, mock.Mock(),
|
||||
[self.router_assoc],
|
||||
events.CREATED)
|
||||
driver_mock.assert_called_once_with(FAKE_BGPSPEAKER_UUID,
|
||||
FAKE_BGP_SPEAKER['local_as'],
|
||||
mock.ANY,
|
||||
mock.ANY, 4)
|
||||
|
||||
@mock.patch.object(priv_driver, 'advertise_routes')
|
||||
def test_handle_update_router_assoc_with_advertise_extra_routes(
|
||||
self, driver_mock):
|
||||
self.bgp_ext._handle_router_notification(self.context, mock.Mock(),
|
||||
[self.router_assoc],
|
||||
events.UPDATED)
|
||||
driver_mock.assert_called_once_with(tuple(FAKE_ROUTES))
|
||||
|
||||
@mock.patch.object(priv_driver, 'withdraw_routes')
|
||||
def test_handle_update_router_assoc_without_advertise_extra_routes(
|
||||
self, driver_mock):
|
||||
assoc_info = {'id': FAKE_ROUTER_ASSOC_UUID,
|
||||
'router_id': self.router_id,
|
||||
'bgp_speaker_id': FAKE_BGPSPEAKER_UUID,
|
||||
'advertise_extra_routes': False}
|
||||
router_assoc = assoc_objects.BgpSpeakerPeerAssociation(
|
||||
self.context, **assoc_info)
|
||||
self.bgp_ext._handle_router_notification(self.context, mock.Mock(),
|
||||
[router_assoc],
|
||||
events.UPDATED)
|
||||
driver_mock.assert_called_once_with(tuple(FAKE_EXTRA_ROUTE))
|
||||
|
||||
@mock.patch.object(priv_driver, 'del_bgp_speaker')
|
||||
def test_handle_delete_router_assoc(self, driver_mock):
|
||||
self.bgp_ext._handle_router_notification(self.context, mock.Mock(),
|
||||
[self.router_assoc],
|
||||
events.DELETED)
|
||||
driver_mock.assert_called_once_with(FAKE_BGPSPEAKER_UUID, mock.ANY)
|
||||
|
||||
@mock.patch.object(priv_driver, 'advertise_routes')
|
||||
@mock.patch.object(priv_driver, 'add_bgp_neighbor')
|
||||
def test_handle_create_peer_assoc(self, driver_mock, advertise_mock):
|
||||
priv_driver.add_bgp_speaker = mock.Mock()
|
||||
self.bgp_ext._handle_router_notification(self.context, mock.Mock(),
|
||||
[self.router_assoc],
|
||||
events.CREATED)
|
||||
self.bgp_ext._handle_peer_notification(self.context, mock.Mock(),
|
||||
[self.peer_assoc],
|
||||
events.CREATED)
|
||||
driver_mock.assert_called_once_with(FAKE_BGPSPEAKER_UUID,
|
||||
FAKE_BGP_PEER['peer_ip'],
|
||||
FAKE_BGP_PEER['remote_as'],
|
||||
mock.ANY)
|
||||
advertise_mock.assert_called_once_with(tuple(FAKE_ROUTES))
|
||||
|
||||
@mock.patch.object(priv_driver, 'del_bgp_neighbor')
|
||||
def test_handle_delete_peer_assoc(self, driver_mock):
|
||||
self.bgp_ext._handle_peer_notification(self.context, mock.Mock(),
|
||||
[self.peer_assoc],
|
||||
events.DELETED)
|
||||
driver_mock.assert_called_once_with(FAKE_BGPSPEAKER_UUID,
|
||||
FAKE_BGP_PEER['peer_ip'],
|
||||
mock.ANY)
|
||||
|
||||
@mock.patch.object(priv_driver, 'advertise_routes')
|
||||
@mock.patch.object(priv_driver, 'withdraw_routes')
|
||||
def test_update_router(self, mock_withdraw, mock_advertise):
|
||||
priv_driver.add_bgp_speaker = mock.Mock()
|
||||
priv_driver.add_bgp_neighbor = mock.Mock()
|
||||
self.bgp_ext._handle_router_notification(self.context, mock.Mock(),
|
||||
[self.router_assoc],
|
||||
events.CREATED)
|
||||
self.bgp_ext._handle_peer_notification(self.context, mock.Mock(),
|
||||
[self.peer_assoc],
|
||||
events.CREATED)
|
||||
new_route = ['155.0.0.0/24']
|
||||
self.plugin_rpc.get_routes_to_advertise.return_value = new_route
|
||||
self.bgp_ext.update_router(self.context, self.router)
|
||||
mock_advertise.assert_called_with(tuple(new_route))
|
||||
mock_withdraw.assert_called_with(tuple(FAKE_ROUTES))
|
Loading…
Reference in New Issue