Remove BGP code from neutron
Once the spinout is undergoing we should perform the eviction. Partially-implements: blueprint bgp-spinout Depends-on: I8be510153edbc496575cde34943ca4c56645e0fb Change-Id: I20b6ddd37d10eae70e8294d578e53137c0f866fechanges/18/306618/10
parent
78fff41ee3
commit
e8d3626d1c
|
@ -1,29 +0,0 @@
|
|||
function configure_bgp_service_plugin {
|
||||
_neutron_service_plugin_class_add "bgp"
|
||||
}
|
||||
|
||||
function configure_bgp {
|
||||
configure_bgp_service_plugin
|
||||
}
|
||||
|
||||
function configure_bgp_dragent {
|
||||
cp $NEUTRON_DIR/etc/bgp_dragent.ini.sample $Q_BGP_DRAGENT_CONF_FILE
|
||||
|
||||
iniset $Q_BGP_DRAGENT_CONF_FILE DEFAULT verbose True
|
||||
iniset $Q_BGP_DRAGENT_CONF_FILE DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
|
||||
if [ -n "$BGP_ROUTER_ID" ]; then
|
||||
iniset $Q_BGP_DRAGENT_CONF_FILE BGP bgp_router_id $BGP_ROUTER_ID
|
||||
fi
|
||||
if [ -z "$BGP_SPEAKER_DRIVER" ]; then
|
||||
BGP_SPEAKER_DRIVER=$RYU_BGP_SPEAKER_DRIVER
|
||||
fi
|
||||
iniset $Q_BGP_DRAGENT_CONF_FILE BGP bgp_speaker_driver $BGP_SPEAKER_DRIVER
|
||||
}
|
||||
|
||||
function start_bgp_dragent {
|
||||
run_process q-bgp-agt "$AGENT_BGP_BINARY --config-file $NEUTRON_CONF --config-file /$Q_BGP_DRAGENT_CONF_FILE"
|
||||
}
|
||||
|
||||
function stop_bgp_dragent {
|
||||
stop_process q-bgp-agt
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
LIBDIR=$DEST/neutron/devstack/lib
|
||||
|
||||
source $LIBDIR/bgp
|
||||
source $LIBDIR/flavors
|
||||
source $LIBDIR/l2_agent
|
||||
source $LIBDIR/l2_agent_sriovnicswitch
|
||||
|
@ -19,9 +18,6 @@ if [[ "$1" == "stack" ]]; then
|
|||
if is_service_enabled q-qos; then
|
||||
configure_qos
|
||||
fi
|
||||
if is_service_enabled q-bgp; then
|
||||
configure_bgp
|
||||
fi
|
||||
if [[ "$Q_AGENT" == "openvswitch" ]] && \
|
||||
[[ "$Q_BUILD_OVS_FROM_GIT" == "True" ]]; then
|
||||
remove_ovs_packages
|
||||
|
@ -33,9 +29,6 @@ if [[ "$1" == "stack" ]]; then
|
|||
if is_service_enabled q-agt; then
|
||||
configure_l2_agent
|
||||
fi
|
||||
if is_service_enabled q-bgp && is_service_enabled q-bgp-agt; then
|
||||
configure_bgp_dragent
|
||||
fi
|
||||
#Note: sriov agent should run with OVS or linux bridge agent
|
||||
#because they are the mechanisms that bind the DHCP and router ports.
|
||||
#Currently devstack lacks the option to run two agents on the same node.
|
||||
|
@ -51,16 +44,10 @@ if [[ "$1" == "stack" ]]; then
|
|||
if is_service_enabled q-sriov-agt; then
|
||||
start_l2_agent_sriov
|
||||
fi
|
||||
if is_service_enabled q-bgp && is_service_enabled q-bgp-agt; then
|
||||
start_bgp_dragent
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
elif [[ "$1" == "unstack" ]]; then
|
||||
if is_service_enabled q-sriov-agt; then
|
||||
stop_l2_agent_sriov
|
||||
fi
|
||||
if is_service_enabled q-bgp && is_service_enabled q-bgp-agt; then
|
||||
stop_bgp_dragent
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -1,8 +1 @@
|
|||
L2_AGENT_EXTENSIONS=${L2_AGENT_EXTENSIONS:-}
|
||||
|
||||
#BGP binary and config information
|
||||
AGENT_BGP_BINARY=${AGENT_BGP_BINARY:-"$NEUTRON_BIN_DIR/neutron-bgp-dragent"}
|
||||
Q_BGP_DRAGENT_CONF_FILE=${Q_BGP_DRAGENT_CONF_FILE:-"$NEUTRON_CONF_DIR/bgp_dragent.ini"}
|
||||
BGP_ROUTER_ID=${BGP_ROUTER_ID:-}
|
||||
|
||||
RYU_BGP_SPEAKER_DRIVER="neutron.services.bgp.driver.ryu.driver.RyuBgpDriver"
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
[DEFAULT]
|
||||
output_file = etc/bgp_dragent.ini.sample
|
||||
wrap_width = 79
|
||||
|
||||
namespace = neutron.base.agent
|
||||
namespace = neutron.bgp.agent
|
||||
namespace = oslo.log
|
|
@ -213,26 +213,8 @@
|
|||
"get_flavor_service_profile": "rule:regular_user",
|
||||
"get_auto_allocated_topology": "rule:admin_or_owner",
|
||||
|
||||
"get_bgp_speaker": "rule:admin_only",
|
||||
"create_bgp_speaker": "rule:admin_only",
|
||||
"update_bgp_speaker": "rule:admin_only",
|
||||
"delete_bgp_speaker": "rule:admin_only",
|
||||
|
||||
"get_bgp_peer": "rule:admin_only",
|
||||
"create_bgp_peer": "rule:admin_only",
|
||||
"update_bgp_peer": "rule:admin_only",
|
||||
"delete_bgp_peer": "rule:admin_only",
|
||||
|
||||
"add_bgp_peer": "rule:admin_only",
|
||||
"remove_bgp_peer": "rule:admin_only",
|
||||
|
||||
"add_gateway_network": "rule:admin_only",
|
||||
"remove_gateway_network": "rule:admin_only",
|
||||
|
||||
"get_advertised_routes":"rule:admin_only",
|
||||
|
||||
"add_bgp_speaker_to_dragent": "rule:admin_only",
|
||||
"remove_bgp_speaker_from_dragent": "rule:admin_only",
|
||||
"list_bgp_speaker_on_dragent": "rule:admin_only",
|
||||
"list_dragent_hosting_bgp_speaker": "rule:admin_only"
|
||||
"get_advertised_routes":"rule:admin_only"
|
||||
}
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
#
|
||||
# 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 oslo_messaging
|
||||
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron.services.bgp.common import constants as bgp_consts
|
||||
|
||||
|
||||
class BgpDrAgentNotifyApi(object):
|
||||
"""API for plugin to notify BGP DrAgent.
|
||||
|
||||
This class implements the client side of an rpc interface. The server side
|
||||
is neutron.services.bgp_speaker.agent.bgp_dragent.BgpDrAgent. For more
|
||||
information about rpc interfaces, please see doc/source/devref/rpc_api.rst.
|
||||
"""
|
||||
|
||||
def __init__(self, topic=bgp_consts.BGP_DRAGENT):
|
||||
target = oslo_messaging.Target(topic=topic, version='1.0')
|
||||
self.client = n_rpc.get_client(target)
|
||||
self.topic = topic
|
||||
|
||||
def bgp_routes_advertisement(self, context, bgp_speaker_id,
|
||||
routes, host):
|
||||
"""Tell BgpDrAgent to begin advertising the given route.
|
||||
|
||||
Invoked on FIP association, adding router port to a tenant network,
|
||||
and new DVR port-host bindings, and subnet creation(?).
|
||||
"""
|
||||
self._notification_host_cast(context, 'bgp_routes_advertisement_end',
|
||||
{'advertise_routes': {'speaker_id': bgp_speaker_id,
|
||||
'routes': routes}}, host)
|
||||
|
||||
def bgp_routes_withdrawal(self, context, bgp_speaker_id,
|
||||
routes, host):
|
||||
"""Tell BgpDrAgent to stop advertising the given route.
|
||||
|
||||
Invoked on FIP disassociation, removal of a router port on a
|
||||
network, and removal of DVR port-host binding, and subnet delete(?).
|
||||
"""
|
||||
self._notification_host_cast(context, 'bgp_routes_withdrawal_end',
|
||||
{'withdraw_routes': {'speaker_id': bgp_speaker_id,
|
||||
'routes': routes}}, host)
|
||||
|
||||
def bgp_peer_disassociated(self, context, bgp_speaker_id,
|
||||
bgp_peer_ip, host):
|
||||
"""Tell BgpDrAgent about a new BGP Peer association.
|
||||
|
||||
This effectively tells the BgpDrAgent to stop a peering session.
|
||||
"""
|
||||
self._notification_host_cast(context, 'bgp_peer_disassociation_end',
|
||||
{'bgp_peer': {'speaker_id': bgp_speaker_id,
|
||||
'peer_ip': bgp_peer_ip}}, host)
|
||||
|
||||
def bgp_peer_associated(self, context, bgp_speaker_id,
|
||||
bgp_peer_id, host):
|
||||
"""Tell BgpDrAgent about a BGP Peer disassociation.
|
||||
|
||||
This effectively tells the bgp_dragent to open a peering session.
|
||||
"""
|
||||
self._notification_host_cast(context, 'bgp_peer_association_end',
|
||||
{'bgp_peer': {'speaker_id': bgp_speaker_id,
|
||||
'peer_id': bgp_peer_id}}, host)
|
||||
|
||||
def bgp_speaker_created(self, context, bgp_speaker_id, host):
|
||||
"""Tell BgpDrAgent about the creation of a BGP Speaker.
|
||||
|
||||
Because a BGP Speaker can be created with BgpPeer binding in place,
|
||||
we need to inform the BgpDrAgent of a new BGP Speaker in case a
|
||||
peering session needs to opened immediately.
|
||||
"""
|
||||
self._notification_host_cast(context, 'bgp_speaker_create_end',
|
||||
{'bgp_speaker': {'id': bgp_speaker_id}}, host)
|
||||
|
||||
def bgp_speaker_removed(self, context, bgp_speaker_id, host):
|
||||
"""Tell BgpDrAgent about the removal of a BGP Speaker.
|
||||
|
||||
Because a BGP Speaker can be removed with BGP Peer binding in
|
||||
place, we need to inform the BgpDrAgent of the removal of a
|
||||
BGP Speaker in case peering sessions need to be stopped.
|
||||
"""
|
||||
self._notification_host_cast(context, 'bgp_speaker_remove_end',
|
||||
{'bgp_speaker': {'id': bgp_speaker_id}}, host)
|
||||
|
||||
def _notification_host_cast(self, context, method, payload, host):
|
||||
"""Send payload to BgpDrAgent in the cast mode"""
|
||||
cctxt = self.client.prepare(topic=self.topic, server=host)
|
||||
cctxt.cast(context, method, payload=payload)
|
||||
|
||||
def _notification_host_call(self, context, method, payload, host):
|
||||
"""Send payload to BgpDrAgent in the call mode"""
|
||||
cctxt = self.client.prepare(topic=self.topic, server=host)
|
||||
cctxt.call(context, method, payload=payload)
|
|
@ -1,65 +0,0 @@
|
|||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
#
|
||||
# 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 oslo_messaging
|
||||
|
||||
from neutron.extensions import bgp as bgp_ext
|
||||
from neutron import manager
|
||||
|
||||
|
||||
class BgpSpeakerRpcCallback(object):
|
||||
"""BgpDrAgent RPC callback in plugin implementations.
|
||||
|
||||
This class implements the server side of an RPC interface.
|
||||
The client side of this interface can be found in
|
||||
neutron.services.bgp_speaker.agent.bgp_dragent.BgpDrPluginApi.
|
||||
For more information about changing RPC interfaces,
|
||||
see doc/source/devref/rpc_api.rst.
|
||||
"""
|
||||
|
||||
# API version history:
|
||||
# 1.0 BGPDRPluginApi BASE_RPC_API_VERSION
|
||||
target = oslo_messaging.Target(version='1.0')
|
||||
|
||||
@property
|
||||
def plugin(self):
|
||||
if not hasattr(self, '_plugin'):
|
||||
self._plugin = manager.NeutronManager.get_service_plugins().get(
|
||||
bgp_ext.BGP_EXT_ALIAS)
|
||||
return self._plugin
|
||||
|
||||
def get_bgp_speaker_info(self, context, bgp_speaker_id):
|
||||
"""Return BGP Speaker details such as peer list and local_as.
|
||||
|
||||
Invoked by the BgpDrAgent to lookup the details of a BGP Speaker.
|
||||
"""
|
||||
return self.plugin.get_bgp_speaker_with_advertised_routes(
|
||||
context, bgp_speaker_id)
|
||||
|
||||
def get_bgp_peer_info(self, context, bgp_peer_id):
|
||||
"""Return BgpPeer details such as IP, remote_as, and credentials.
|
||||
|
||||
Invoked by the BgpDrAgent to lookup the details of a BGP peer.
|
||||
"""
|
||||
return self.plugin.get_bgp_peer(context, bgp_peer_id,
|
||||
['peer_ip', 'remote_as',
|
||||
'auth_type', 'password'])
|
||||
|
||||
def get_bgp_speakers(self, context, host=None, **kwargs):
|
||||
"""Returns the list of all BgpSpeakers.
|
||||
|
||||
Typically invoked by the BgpDrAgent as part of its bootstrap process.
|
||||
"""
|
||||
return self.plugin.get_bgp_speakers_for_agent_host(context, host)
|
|
@ -1,20 +0,0 @@
|
|||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
#
|
||||
# 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.services.bgp.agent import entry as bgp_dragent
|
||||
|
||||
|
||||
def main():
|
||||
bgp_dragent.main()
|
1009
neutron/db/bgp_db.py
1009
neutron/db/bgp_db.py
File diff suppressed because it is too large
Load Diff
|
@ -1,215 +0,0 @@
|
|||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
# 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_db import exception as db_exc
|
||||
from oslo_log import log as logging
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy.orm import exc
|
||||
|
||||
from neutron._i18n import _
|
||||
from neutron._i18n import _LW
|
||||
from neutron.db import agents_db
|
||||
from neutron.db import agentschedulers_db as as_db
|
||||
from neutron.db import model_base
|
||||
from neutron.extensions import bgp_dragentscheduler as bgp_dras_ext
|
||||
from neutron.services.bgp.common import constants as bgp_consts
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
BGP_DRAGENT_SCHEDULER_OPTS = [
|
||||
cfg.StrOpt(
|
||||
'bgp_drscheduler_driver',
|
||||
default='neutron.services.bgp.scheduler'
|
||||
'.bgp_dragent_scheduler.ChanceScheduler',
|
||||
help=_('Driver used for scheduling BGP speakers to BGP DrAgent'))
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(BGP_DRAGENT_SCHEDULER_OPTS)
|
||||
|
||||
|
||||
class BgpSpeakerDrAgentBinding(model_base.BASEV2):
|
||||
"""Represents a mapping between BGP speaker and BGP DRAgent"""
|
||||
|
||||
__tablename__ = 'bgp_speaker_dragent_bindings'
|
||||
|
||||
bgp_speaker_id = sa.Column(sa.String(length=36),
|
||||
sa.ForeignKey("bgp_speakers.id",
|
||||
ondelete='CASCADE'),
|
||||
nullable=False)
|
||||
dragent = orm.relation(agents_db.Agent)
|
||||
agent_id = sa.Column(sa.String(length=36),
|
||||
sa.ForeignKey("agents.id",
|
||||
ondelete='CASCADE'),
|
||||
primary_key=True)
|
||||
|
||||
|
||||
class BgpDrAgentSchedulerDbMixin(bgp_dras_ext.BgpDrSchedulerPluginBase,
|
||||
as_db.AgentSchedulerDbMixin):
|
||||
|
||||
bgp_drscheduler = None
|
||||
|
||||
def schedule_unscheduled_bgp_speakers(self, context, host):
|
||||
if self.bgp_drscheduler:
|
||||
return self.bgp_drscheduler.schedule_unscheduled_bgp_speakers(
|
||||
context, host)
|
||||
else:
|
||||
LOG.warning(_LW("Cannot schedule BgpSpeaker to DrAgent. "
|
||||
"Reason: No scheduler registered."))
|
||||
|
||||
def schedule_bgp_speaker(self, context, created_bgp_speaker):
|
||||
if self.bgp_drscheduler:
|
||||
agents = self.bgp_drscheduler.schedule(context,
|
||||
created_bgp_speaker)
|
||||
for agent in agents:
|
||||
self._bgp_rpc.bgp_speaker_created(context,
|
||||
created_bgp_speaker['id'],
|
||||
agent.host)
|
||||
else:
|
||||
LOG.warning(_LW("Cannot schedule BgpSpeaker to DrAgent. "
|
||||
"Reason: No scheduler registered."))
|
||||
|
||||
def add_bgp_speaker_to_dragent(self, context, agent_id, speaker_id):
|
||||
"""Associate a BgpDrAgent with a BgpSpeaker."""
|
||||
try:
|
||||
self._save_bgp_speaker_dragent_binding(context,
|
||||
agent_id,
|
||||
speaker_id)
|
||||
except db_exc.DBDuplicateEntry:
|
||||
raise bgp_dras_ext.DrAgentAssociationError(
|
||||
agent_id=agent_id)
|
||||
|
||||
LOG.debug('BgpSpeaker %(bgp_speaker_id)s added to '
|
||||
'BgpDrAgent %(agent_id)s',
|
||||
{'bgp_speaker_id': speaker_id, 'agent_id': agent_id})
|
||||
|
||||
def _save_bgp_speaker_dragent_binding(self, context,
|
||||
agent_id, speaker_id):
|
||||
with context.session.begin(subtransactions=True):
|
||||
agent_db = self._get_agent(context, agent_id)
|
||||
agent_up = agent_db['admin_state_up']
|
||||
is_agent_bgp = (agent_db['agent_type'] ==
|
||||
bgp_consts.AGENT_TYPE_BGP_ROUTING)
|
||||
if not is_agent_bgp or not agent_up:
|
||||
raise bgp_dras_ext.DrAgentInvalid(id=agent_id)
|
||||
|
||||
binding = BgpSpeakerDrAgentBinding()
|
||||
binding.bgp_speaker_id = speaker_id
|
||||
binding.agent_id = agent_id
|
||||
context.session.add(binding)
|
||||
|
||||
self._bgp_rpc.bgp_speaker_created(context, speaker_id, agent_db.host)
|
||||
|
||||
def remove_bgp_speaker_from_dragent(self, context, agent_id, speaker_id):
|
||||
with context.session.begin(subtransactions=True):
|
||||
agent_db = self._get_agent(context, agent_id)
|
||||
is_agent_bgp = (agent_db['agent_type'] ==
|
||||
bgp_consts.AGENT_TYPE_BGP_ROUTING)
|
||||
if not is_agent_bgp:
|
||||
raise bgp_dras_ext.DrAgentInvalid(id=agent_id)
|
||||
|
||||
query = context.session.query(BgpSpeakerDrAgentBinding)
|
||||
query = query.filter_by(bgp_speaker_id=speaker_id,
|
||||
agent_id=agent_id)
|
||||
|
||||
num_deleted = query.delete()
|
||||
if not num_deleted:
|
||||
raise bgp_dras_ext.DrAgentNotHostingBgpSpeaker(
|
||||
bgp_speaker_id=speaker_id,
|
||||
agent_id=agent_id)
|
||||
LOG.debug('BgpSpeaker %(bgp_speaker_id)s removed from '
|
||||
'BgpDrAgent %(agent_id)s',
|
||||
{'bgp_speaker_id': speaker_id,
|
||||
'agent_id': agent_id})
|
||||
|
||||
self._bgp_rpc.bgp_speaker_removed(context, speaker_id, agent_db.host)
|
||||
|
||||
def get_dragents_hosting_bgp_speakers(self, context, bgp_speaker_ids,
|
||||
active=None, admin_state_up=None):
|
||||
query = context.session.query(BgpSpeakerDrAgentBinding)
|
||||
query = query.options(orm.contains_eager(
|
||||
BgpSpeakerDrAgentBinding.dragent))
|
||||
query = query.join(BgpSpeakerDrAgentBinding.dragent)
|
||||
|
||||
if len(bgp_speaker_ids) == 1:
|
||||
query = query.filter(
|
||||
BgpSpeakerDrAgentBinding.bgp_speaker_id == (
|
||||
bgp_speaker_ids[0]))
|
||||
elif bgp_speaker_ids:
|
||||
query = query.filter(
|
||||
BgpSpeakerDrAgentBinding.bgp_speaker_id in bgp_speaker_ids)
|
||||
if admin_state_up is not None:
|
||||
query = query.filter(agents_db.Agent.admin_state_up ==
|
||||
admin_state_up)
|
||||
|
||||
return [binding.dragent
|
||||
for binding in query
|
||||
if as_db.AgentSchedulerDbMixin.is_eligible_agent(
|
||||
active, binding.dragent)]
|
||||
|
||||
def get_dragent_bgp_speaker_bindings(self, context):
|
||||
return context.session.query(BgpSpeakerDrAgentBinding).all()
|
||||
|
||||
def list_dragent_hosting_bgp_speaker(self, context, speaker_id):
|
||||
dragents = self.get_dragents_hosting_bgp_speakers(context,
|
||||
[speaker_id])
|
||||
agent_ids = [dragent.id for dragent in dragents]
|
||||
if not agent_ids:
|
||||
return {'agents': []}
|
||||
return {'agents': self.get_agents(context, filters={'id': agent_ids})}
|
||||
|
||||
def list_bgp_speaker_on_dragent(self, context, agent_id):
|
||||
query = context.session.query(BgpSpeakerDrAgentBinding.bgp_speaker_id)
|
||||
query = query.filter_by(agent_id=agent_id)
|
||||
|
||||
bgp_speaker_ids = [item[0] for item in query]
|
||||
if not bgp_speaker_ids:
|
||||
# Exception will be thrown if the requested agent does not exist.
|
||||
self._get_agent(context, agent_id)
|
||||
return {'bgp_speakers': []}
|
||||
return {'bgp_speakers':
|
||||
self.get_bgp_speakers(context,
|
||||
filters={'id': bgp_speaker_ids})}
|
||||
|
||||
def get_bgp_speakers_for_agent_host(self, context, host):
|
||||
agent = self._get_agent_by_type_and_host(
|
||||
context, bgp_consts.AGENT_TYPE_BGP_ROUTING, host)
|
||||
if not agent.admin_state_up:
|
||||
return {}
|
||||
|
||||
query = context.session.query(BgpSpeakerDrAgentBinding)
|
||||
query = query.filter(BgpSpeakerDrAgentBinding.agent_id == agent.id)
|
||||
try:
|
||||
binding = query.one()
|
||||
except exc.NoResultFound:
|
||||
return []
|
||||
bgp_speaker = self.get_bgp_speaker_with_advertised_routes(
|
||||
context, binding['bgp_speaker_id'])
|
||||
return [bgp_speaker]
|
||||
|
||||
def get_bgp_speaker_by_speaker_id(self, context, bgp_speaker_id):
|
||||
try:
|
||||
return self.get_bgp_speaker(context, bgp_speaker_id)
|
||||
except exc.NoResultFound:
|
||||
return {}
|
||||
|
||||
def get_bgp_peer_by_peer_id(self, context, bgp_peer_id):
|
||||
try:
|
||||
return self.get_bgp_peer(context, bgp_peer_id)
|
||||
except exc.NoResultFound:
|
||||
return {}
|
|
@ -31,6 +31,15 @@ REPO_ARISTA_TABLES = [
|
|||
'arista_provisioned_tenants',
|
||||
]
|
||||
|
||||
# BGP models in openstack/neutron-dynamic-routing
|
||||
REPO_NEUTRON_DYNAMIC_ROUTING_TABLES = [
|
||||
'bgp_speakers',
|
||||
'bgp_peers',
|
||||
'bgp_speaker_network_bindings',
|
||||
'bgp_speaker_peer_bindings',
|
||||
'bgp_speaker_dragent_bindings',
|
||||
]
|
||||
|
||||
# Models moved to openstack/networking-cisco
|
||||
REPO_CISCO_TABLES = [
|
||||
'cisco_ml2_apic_contracts',
|
||||
|
@ -113,6 +122,7 @@ REPO_NUAGE_TABLES = [
|
|||
|
||||
TABLES = (FWAAS_TABLES + LBAAS_TABLES + VPNAAS_TABLES +
|
||||
REPO_ARISTA_TABLES +
|
||||
REPO_NEUTRON_DYNAMIC_ROUTING_TABLES +
|
||||
REPO_CISCO_TABLES +
|
||||
REPO_VMWARE_TABLES +
|
||||
REPO_BROCADE_TABLES +
|
||||
|
|
|
@ -25,8 +25,6 @@ from neutron.db import address_scope_db # noqa
|
|||
from neutron.db import agents_db # noqa
|
||||
from neutron.db import agentschedulers_db # noqa
|
||||
from neutron.db import allowedaddresspairs_db # noqa
|
||||
from neutron.db import bgp_db # noqa
|
||||
from neutron.db import bgp_dragentscheduler_db # noqa
|
||||
from neutron.db import dns_db # noqa
|
||||
from neutron.db import dvr_mac_db # noqa
|
||||
from neutron.db import external_net_db # noqa
|
||||
|
|
|
@ -1,208 +0,0 @@
|
|||
# Copyright 2016 Hewlett Packard Development Coompany LP
|
||||
#
|
||||
# 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_lib.api import converters
|
||||
from neutron_lib import exceptions
|
||||
|
||||
from neutron._i18n import _
|
||||
from neutron.api import extensions
|
||||
from neutron.api.v2 import attributes as attr
|
||||
from neutron.api.v2 import resource_helper as rh
|
||||
from neutron.services.bgp.common import constants as bgp_consts
|
||||
|
||||
BGP_EXT_ALIAS = 'bgp'
|
||||
BGP_SPEAKER_RESOURCE_NAME = 'bgp-speaker'
|
||||
BGP_SPEAKER_BODY_KEY_NAME = 'bgp_speaker'
|
||||
BGP_PEER_BODY_KEY_NAME = 'bgp_peer'
|
||||
|
||||
|
||||
RESOURCE_ATTRIBUTE_MAP = {
|
||||
BGP_SPEAKER_RESOURCE_NAME + 's': {
|
||||
'id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': True, 'primary_key': True},
|
||||
'name': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string': attr.NAME_MAX_LEN},
|
||||
'is_visible': True, 'default': ''},
|
||||
'local_as': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:range': (bgp_consts.MIN_ASNUM,
|
||||
bgp_consts.MAX_ASNUM)},
|
||||
'is_visible': True, 'default': None,
|
||||
'required_by_policy': False,
|
||||
'enforce_policy': False},
|
||||
'ip_version': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:values': [4, 6]},
|
||||
'is_visible': True, 'default': None,
|
||||
'required_by_policy': False,
|
||||
'enforce_policy': False},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'required_by_policy': False,
|
||||
'validate': {'type:string': attr.TENANT_ID_MAX_LEN},
|
||||
'is_visible': True},
|
||||
'peers': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid_list': None},
|
||||
'is_visible': True, 'default': [],
|
||||
'required_by_policy': False,
|
||||
'enforce_policy': True},
|
||||
'networks': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid_list': None},
|
||||
'is_visible': True, 'default': [],
|
||||
'required_by_policy': False,
|
||||
'enforce_policy': True},
|
||||
'advertise_floating_ip_host_routes': {
|
||||
'allow_post': True,
|
||||
'allow_put': True,
|
||||
'convert_to': converters.convert_to_boolean,
|
||||
'validate': {'type:boolean': None},
|
||||
'is_visible': True, 'default': True,
|
||||
'required_by_policy': False,
|
||||
'enforce_policy': True},
|
||||
'advertise_tenant_networks': {
|
||||
'allow_post': True,
|
||||
'allow_put': True,
|
||||
'convert_to': converters.convert_to_boolean,
|
||||
'validate': {'type:boolean': None},
|
||||
'is_visible': True, 'default': True,
|
||||
'required_by_policy': False,
|
||||
'enforce_policy': True},
|
||||
},
|
||||
'bgp-peers': {
|
||||
'id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': True, 'primary_key': True},
|
||||
'name': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string': attr.NAME_MAX_LEN},
|
||||
'is_visible': True, 'default': ''},
|
||||
'peer_ip': {'allow_post': True, 'allow_put': False,
|
||||
'required_by_policy': True,
|
||||
'validate': {'type:ip_address': None},
|
||||
'is_visible': True},
|
||||
'remote_as': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:range': (bgp_consts.MIN_ASNUM,
|
||||
bgp_consts.MAX_ASNUM)},
|
||||
'is_visible': True, 'default': None,
|
||||
'required_by_policy': False,
|
||||
'enforce_policy': False},
|
||||
'auth_type': {'allow_post': True, 'allow_put': False,
|
||||
'required_by_policy': True,
|
||||
'validate': {'type:values':
|
||||
bgp_consts.SUPPORTED_AUTH_TYPES},
|
||||
'is_visible': True},
|
||||
'password': {'allow_post': True, 'allow_put': True,
|
||||
'required_by_policy': True,
|
||||
'validate': {'type:string_or_none': None},
|
||||
'is_visible': False,
|
||||
'default': None},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'required_by_policy': False,
|
||||
'validate': {'type:string': attr.TENANT_ID_MAX_LEN},
|
||||
'is_visible': True}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Dynamic Routing Exceptions
|
||||
class BgpSpeakerNotFound(exceptions.NotFound):
|
||||
message = _("BGP speaker %(id)s could not be found.")
|
||||
|
||||
|
||||
class BgpPeerNotFound(exceptions.NotFound):
|
||||
message = _("BGP peer %(id)s could not be found.")
|
||||
|
||||
|
||||
class BgpPeerNotAuthenticated(exceptions.NotFound):
|
||||
message = _("BGP peer %(bgp_peer_id)s not authenticated.")
|
||||
|
||||
|
||||
class BgpSpeakerPeerNotAssociated(exceptions.NotFound):
|
||||
message = _("BGP peer %(bgp_peer_id)s is not associated with "
|
||||
"BGP speaker %(bgp_speaker_id)s.")
|
||||
|
||||
|
||||
class BgpSpeakerNetworkNotAssociated(exceptions.NotFound):
|
||||
message = _("Network %(network_id)s is not associated with "
|
||||
"BGP speaker %(bgp_speaker_id)s.")
|
||||
|
||||
|
||||
class BgpSpeakerNetworkBindingError(exceptions.Conflict):
|
||||
message = _("Network %(network_id)s is already bound to BgpSpeaker "
|
||||
"%(bgp_speaker_id)s.")
|
||||
|
||||
|
||||
class NetworkNotBound(exceptions.NotFound):
|
||||
message = _("Network %(network_id)s is not bound to a BgpSpeaker.")
|
||||
|
||||
|
||||
class DuplicateBgpPeerIpException(exceptions.Conflict):
|
||||
_message = _("BGP Speaker %(bgp_speaker_id)s is already configured to "
|
||||
"peer with a BGP Peer at %(peer_ip)s, it cannot peer with "
|
||||
"BGP Peer %(bgp_peer_id)s.")
|
||||
|
||||
|
||||
class InvalidBgpPeerMd5Authentication(exceptions.BadRequest):
|
||||
message = _("A password must be supplied when using auth_type md5.")
|
||||
|
||||
|
||||
class NetworkNotBoundForIpVersion(NetworkNotBound):
|
||||
message = _("Network %(network_id)s is not bound to a IPv%(ip_version)s "
|
||||
"BgpSpeaker.")
|
||||
|
||||
|
||||
class Bgp(extensions.ExtensionDescriptor):
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Neutron BGP Dynamic Routing Extension"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return BGP_EXT_ALIAS
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return("Discover and advertise routes for Neutron prefixes "
|
||||
"dynamically via BGP")
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2014-07-01T15:37:00-00:00"
|
||||
|
||||
@classmethod
|
||||
def get_resources(cls):
|
||||
plural_mappings = rh.build_plural_mappings(
|
||||
{}, RESOURCE_ATTRIBUTE_MAP)
|
||||
attr.PLURALS.update(plural_mappings)
|
||||
action_map = {BGP_SPEAKER_RESOURCE_NAME:
|
||||
{'add_bgp_peer': 'PUT',
|
||||
'remove_bgp_peer': 'PUT',
|
||||
'add_gateway_network': 'PUT',
|
||||
'remove_gateway_network': 'PUT',
|
||||
'get_advertised_routes': 'GET'}}
|
||||
exts = rh.build_resource_info(plural_mappings,
|
||||
RESOURCE_ATTRIBUTE_MAP,
|
||||
BGP_EXT_ALIAS,
|
||||
action_map=action_map)
|
||||
|
||||
return exts
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return RESOURCE_ATTRIBUTE_MAP
|
||||
else:
|
||||
return {}
|
||||
|
||||
def update_attributes_map(self, attributes):
|
||||
super(Bgp, self).update_attributes_map(
|
||||
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
|
|
@ -1,183 +0,0 @@
|
|||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
# 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 abc
|
||||
import six
|
||||
import webob
|
||||
|
||||
from neutron_lib import exceptions
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron.api import extensions
|
||||
from neutron.api.v2 import base
|
||||
from neutron.api.v2 import resource
|
||||
from neutron.extensions import agent
|
||||
from neutron.extensions import bgp as bgp_ext
|
||||
from neutron._i18n import _, _LE
|
||||
from neutron import manager
|
||||
from neutron import wsgi
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
BGP_DRAGENT_SCHEDULER_EXT_ALIAS = 'bgp_dragent_scheduler'
|
||||
BGP_DRINSTANCE = 'bgp-drinstance'
|
||||
BGP_DRINSTANCES = BGP_DRINSTANCE + 's'
|
||||
BGP_DRAGENT = 'bgp-dragent'
|
||||
BGP_DRAGENTS = BGP_DRAGENT + 's'
|
||||
|
||||
|
||||
class DrAgentInvalid(agent.AgentNotFound):
|
||||
message = _("BgpDrAgent %(id)s is invalid or has been disabled.")
|
||||
|
||||
|
||||
class DrAgentNotHostingBgpSpeaker(exceptions.NotFound):
|
||||
message = _("BGP speaker %(bgp_speaker_id)s is not hosted "
|
||||
"by the BgpDrAgent %(agent_id)s.")
|
||||
|
||||
|
||||
class DrAgentAssociationError(exceptions.Conflict):
|
||||
message = _("BgpDrAgent %(agent_id)s is already associated "
|
||||
"to a BGP speaker.")
|
||||
|
||||
|
||||
class BgpDrSchedulerController(wsgi.Controller):
|
||||
"""Schedule BgpSpeaker for a BgpDrAgent"""
|
||||
def get_plugin(self):
|
||||
plugin = manager.NeutronManager.get_service_plugins().get(
|
||||
bgp_ext.BGP_EXT_ALIAS)
|
||||
if not plugin:
|
||||
LOG.error(_LE('No plugin for BGP routing registered'))
|
||||
msg = _('The resource could not be found.')
|
||||
raise webob.exc.HTTPNotFound(msg)
|
||||
return plugin
|
||||
|
||||
def index(self, request, **kwargs):
|
||||
plugin = self.get_plugin()
|
||||
return plugin.list_bgp_speaker_on_dragent(
|
||||
request.context, kwargs['agent_id'])
|
||||
|
||||
def create(self, request, body, **kwargs):
|
||||
plugin = self.get_plugin()
|
||||
return plugin.add_bgp_speaker_to_dragent(
|
||||
request.context,
|
||||
kwargs['agent_id'],
|
||||
body['bgp_speaker_id'])
|
||||
|
||||
def delete(self, request, id, **kwargs):
|
||||
plugin = self.get_plugin()
|
||||
return plugin.remove_bgp_speaker_from_dragent(
|
||||
request.context, kwargs['agent_id'], id)
|
||||
|
||||
|
||||
class BgpDrAgentController(wsgi.Controller):
|
||||
def get_plugin(self):
|
||||
plugin = manager.NeutronManager.get_service_plugins().get(
|
||||
bgp_ext.BGP_EXT_ALIAS)
|
||||
if not plugin:
|
||||
LOG.error(_LE('No plugin for BGP routing registered'))
|
||||
msg = _LE('The resource could not be found.')
|
||||
raise webob.exc.HTTPNotFound(msg)
|
||||
return plugin
|
||||
|
||||
def index(self, request, **kwargs):
|
||||
plugin = manager.NeutronManager.get_service_plugins().get(
|
||||
bgp_ext.BGP_EXT_ALIAS)
|
||||
return plugin.list_dragent_hosting_bgp_speaker(
|
||||
request.context, kwargs['bgp_speaker_id'])
|
||||
|
||||
|
||||
class Bgp_dragentscheduler(extensions.ExtensionDescriptor):
|
||||
"""Extension class supporting Dynamic Routing scheduler.
|
||||
"""
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "BGP Dynamic Routing Agent Scheduler"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return BGP_DRAGENT_SCHEDULER_EXT_ALIAS
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return "Schedules BgpSpeakers on BgpDrAgent"
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2015-07-30T10:00:00-00:00"
|
||||
|
||||
@classmethod
|
||||
def get_resources(cls):
|
||||
"""Returns Ext Resources."""
|
||||
exts = []
|
||||
parent = dict(member_name="agent",
|
||||
collection_name="agents")
|
||||
|
||||
controller = resource.Resource(BgpDrSchedulerController(),
|
||||
base.FAULT_MAP)
|
||||
exts.append(extensions.ResourceExtension(BGP_DRINSTANCES,
|
||||
controller, parent))
|
||||
|
||||
parent = dict(member_name="bgp_speaker",
|
||||
collection_name="bgp-speakers")
|
||||
controller = resource.Resource(BgpDrAgentController(),
|
||||
base.FAULT_MAP)
|
||||
exts.append(extensions.ResourceExtension(BGP_DRAGENTS,
|
||||
controller, parent))
|
||||
return exts
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
return {}
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BgpDrSchedulerPluginBase(object):
|
||||
"""REST API to operate BGP dynamic routing agent scheduler.
|
||||
|
||||
All the methods must be executed in admin context.
|
||||
"""
|
||||
def get_plugin_description(self):
|
||||
return "Neutron BGP dynamic routing scheduler Plugin"
|
||||
|
||||
def get_plugin_type(self):
|
||||
return bgp_ext.BGP_EXT_ALIAS
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_bgp_speaker_to_dragent(self, context, agent_id, speaker_id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def remove_bgp_speaker_from_dragent(self, context, agent_id, speaker_id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_dragent_hosting_bgp_speaker(self, context, speaker_id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_bgp_speaker_on_dragent(self, context, agent_id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_bgp_speakers_for_agent_host(self, context, host):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_bgp_speaker_by_speaker_id(self, context, speaker_id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_bgp_peer_by_peer_id(self, context, bgp_peer_id):
|
||||
pass
|
|
@ -1,707 +0,0 @@
|
|||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
#
|
||||
# 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 collections
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
from oslo_service import loopingcall
|
||||
from oslo_service import periodic_task
|
||||
from oslo_utils import importutils
|
||||
|
||||
from neutron.agent import rpc as agent_rpc
|
||||
from neutron.common import constants
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron.common import topics
|
||||
from neutron.common import utils
|
||||
from neutron import context
|
||||
from neutron.extensions import bgp as bgp_ext
|
||||
from neutron._i18n import _, _LE, _LI, _LW
|
||||
from neutron import manager
|
||||
from neutron.services.bgp.common import constants as bgp_consts
|
||||
from neutron.services.bgp.driver import exceptions as driver_exc
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BgpDrAgent(manager.Manager):
|
||||
"""BGP Dynamic Routing agent service manager.
|
||||
|
||||
Note that the public methods of this class are exposed as the server side
|
||||
of an rpc interface. The neutron server uses
|
||||
neutron.api.rpc.agentnotifiers.bgp_dr_rpc_agent_api.
|
||||
BgpDrAgentNotifyApi as the client side to execute the methods
|
||||
here. For more information about changing rpc interfaces, see
|
||||
doc/source/devref/rpc_api.rst.
|
||||
|
||||
API version history:
|
||||
1.0 initial Version
|
||||
"""
|
||||
target = oslo_messaging.Target(version='1.0')
|
||||
|
||||
def __init__(self, host, conf=None):
|
||||
super(BgpDrAgent, self).__init__()
|
||||
self.initialize_driver(conf)
|
||||
self.needs_resync_reasons = collections.defaultdict(list)
|
||||
self.needs_full_sync_reason = None
|
||||
|
||||
self.cache = BgpSpeakerCache()
|
||||
self.context = context.get_admin_context_without_session()
|
||||
self.plugin_rpc = BgpDrPluginApi(bgp_consts.BGP_PLUGIN,
|
||||
self.context, host)
|
||||
|
||||
def initialize_driver(self, conf):
|
||||
self.conf = conf or cfg.CONF.BGP
|
||||
try:
|
||||
self.dr_driver_cls = (
|
||||
importutils.import_object(self.conf.bgp_speaker_driver,
|
||||
self.conf))
|
||||
except ImportError:
|
||||
LOG.exception(_LE("Error while importing BGP speaker driver %s"),
|
||||
self.conf.bgp_speaker_driver)
|
||||
raise SystemExit(1)
|
||||
|
||||
def _handle_driver_failure(self, bgp_speaker_id, method, driver_exec):
|
||||
self.schedule_resync(reason=driver_exec,
|
||||
speaker_id=bgp_speaker_id)
|
||||
LOG.error(_LE('Call to driver for BGP Speaker %(bgp_speaker)s '
|
||||
'%(method)s has failed with exception '
|
||||
'%(driver_exec)s.'),
|
||||
{'bgp_speaker': bgp_speaker_id,
|
||||
'method': method,
|
||||
'driver_exec': driver_exec})
|
||||
|
||||
def after_start(self):
|
||||
self.run()
|
||||
LOG.info(_LI("BGP Dynamic Routing agent started"))
|
||||
|
||||
def run(self):
|
||||
"""Activate BGP Dynamic Routing agent."""
|
||||
self.sync_state(self.context)
|
||||
self.periodic_resync(self.context)
|
||||
|
||||
@utils.synchronized('bgp-dragent')
|
||||
def sync_state(self, context, full_sync=None, bgp_speakers=None):
|
||||
try:
|
||||
hosted_bgp_speakers = self.plugin_rpc.get_bgp_speakers(context)
|
||||
hosted_bgp_speaker_ids = [bgp_speaker['id']
|
||||
for bgp_speaker in hosted_bgp_speakers]
|
||||
cached_bgp_speakers = self.cache.get_bgp_speaker_ids()
|
||||
for bgp_speaker_id in cached_bgp_speakers:
|
||||
if bgp_speaker_id not in hosted_bgp_speaker_ids:
|
||||
self.remove_bgp_speaker_from_dragent(bgp_speaker_id)
|
||||
|
||||
resync_all = not bgp_speakers or full_sync
|
||||
only_bs = set() if resync_all else set(bgp_speakers)
|
||||
for hosted_bgp_speaker in hosted_bgp_speakers:
|
||||
hosted_bs_id = hosted_bgp_speaker['id']
|
||||
if resync_all or hosted_bs_id in only_bs:
|
||||
if not self.cache.is_bgp_speaker_added(hosted_bs_id):
|
||||
self.safe_configure_dragent_for_bgp_speaker(
|
||||
hosted_bgp_speaker)
|
||||
continue
|
||||
self.sync_bgp_speaker(hosted_bgp_speaker)
|
||||
resync_reason = "Periodic route cache refresh"
|
||||
self.schedule_resync(speaker_id=hosted_bs_id,
|
||||
reason=resync_reason)
|
||||
except Exception as e:
|
||||
self.schedule_full_resync(reason=e)
|
||||
LOG.error(_LE('Unable to sync BGP speaker state.'))
|
||||
|
||||
def sync_bgp_speaker(self, bgp_speaker):
|
||||
# sync BGP Speakers
|
||||
bgp_peer_ips = set(
|
||||
[bgp_peer['peer_ip'] for bgp_peer in bgp_speaker['peers']])
|
||||
cached_bgp_peer_ips = set(
|
||||
self.cache.get_bgp_peer_ips(bgp_speaker['id']))
|
||||
removed_bgp_peer_ips = cached_bgp_peer_ips - bgp_peer_ips
|
||||
|
||||
for bgp_peer_ip in removed_bgp_peer_ips:
|
||||
self.remove_bgp_peer_from_bgp_speaker(bgp_speaker['id'],
|
||||
bgp_peer_ip)
|
||||
if bgp_peer_ips:
|
||||
self.add_bgp_peers_to_bgp_speaker(bgp_speaker)
|
||||
|
||||
# sync advertise routes
|
||||
cached_adv_routes = self.cache.get_adv_routes(bgp_speaker['id'])
|
||||
adv_routes = bgp_speaker['advertised_routes']
|
||||
if cached_adv_routes == adv_routes:
|
||||
return
|
||||
|
||||
for cached_route in cached_adv_routes:
|
||||
if cached_route not in adv_routes:
|
||||
self.withdraw_route_via_bgp_speaker(bgp_speaker['id'],
|
||||
bgp_speaker['local_as'],
|
||||
cached_route)
|
||||
|
||||
self.advertise_routes_via_bgp_speaker(bgp_speaker)
|
||||
|
||||
@utils.exception_logger()
|
||||
def _periodic_resync_helper(self, context):
|
||||
"""Resync the BgpDrAgent state at the configured interval."""
|
||||
if self.needs_resync_reasons or self.needs_full_sync_reason:
|
||||
full_sync = self.needs_full_sync_reason
|
||||
reasons = self.needs_resync_reasons
|
||||
# Reset old reasons
|
||||
self.needs_full_sync_reason = None
|
||||
self.needs_resync_reasons = collections.defaultdict(list)
|
||||
if full_sync:
|
||||
LOG.debug("resync all: %(reason)s", {"reason": full_sync})
|
||||
for bgp_speaker, reason in reasons.items():
|
||||
LOG.debug("resync (%(bgp_speaker)s): %(reason)s",
|
||||
{"reason": reason, "bgp_speaker": bgp_speaker})
|
||||
self.sync_state(
|
||||
context, full_sync=full_sync, bgp_speakers=reasons.keys())
|
||||
|
||||
# NOTE: spacing is set 1 sec. The actual interval is controlled
|
||||
# by neutron/service.py which defaults to CONF.periodic_interval
|
||||
@periodic_task.periodic_task(spacing=1)
|
||||
def periodic_resync(self, context):
|
||||
LOG.debug("Started periodic resync.")
|
||||
self._periodic_resync_helper(context)
|
||||
|
||||
@utils.synchronized('bgp-dr-agent')
|
||||
def bgp_speaker_create_end(self, context, payload):
|
||||
"""Handle bgp_speaker_create_end notification event."""
|
||||
bgp_speaker_id = payload['bgp_speaker']['id']
|
||||
LOG.debug('Received BGP speaker create notification for '
|
||||
'speaker_id=%(speaker_id)s from the neutron server.',
|
||||
{'speaker_id': bgp_speaker_id})
|
||||
self.add_bgp_speaker_helper(bgp_speaker_id)
|
||||
|
||||
@utils.synchronized('bgp-dr-agent')
|
||||
def bgp_speaker_remove_end(self, context, payload):
|
||||
"""Handle bgp_speaker_create_end notification event."""
|
||||
|
||||
bgp_speaker_id = payload['bgp_speaker']['id']
|
||||
LOG.debug('Received BGP speaker remove notification for '
|
||||
'speaker_id=%(speaker_id)s from the neutron server.',
|
||||
{'speaker_id': bgp_speaker_id})
|
||||
self.remove_bgp_speaker_from_dragent(bgp_speaker_id)
|
||||
|
||||
@utils.synchronized('bgp-dr-agent')
|
||||
def bgp_peer_association_end(self, context, payload):
|
||||
"""Handle bgp_peer_association_end notification event."""
|
||||
|
||||
bgp_peer_id = payload['bgp_peer']['peer_id']
|
||||
bgp_speaker_id = payload['bgp_peer']['speaker_id']
|
||||
LOG.debug('Received BGP peer associate notification for '
|
||||
'speaker_id=%(speaker_id)s peer_id=%(peer_id)s '
|
||||
'from the neutron server.',
|
||||
{'speaker_id': bgp_speaker_id,
|
||||
'peer_id': bgp_peer_id})
|
||||
self.add_bgp_peer_helper(bgp_speaker_id, bgp_peer_id)
|
||||
|
||||
@utils.synchronized('bgp-dr-agent')
|
||||
def bgp_peer_disassociation_end(self, context, payload):
|
||||
"""Handle bgp_peer_disassociation_end notification event."""
|
||||
|
||||
bgp_peer_ip = payload['bgp_peer']['peer_ip']
|
||||
bgp_speaker_id = payload['bgp_peer']['speaker_id']
|
||||
LOG.debug('Received BGP peer disassociate notification for '
|
||||
'speaker_id=%(speaker_id)s peer_ip=%(peer_ip)s '
|
||||
'from the neutron server.',
|
||||
{'speaker_id': bgp_speaker_id,
|
||||
'peer_ip': bgp_peer_ip})
|
||||
self.remove_bgp_peer_from_bgp_speaker(bgp_speaker_id, bgp_peer_ip)
|
||||
|
||||
@utils.synchronized('bgp-dr-agent')
|
||||
def bgp_routes_advertisement_end(self, context, payload):
|
||||
"""Handle bgp_routes_advertisement_end notification event."""
|
||||
|
||||
bgp_speaker_id = payload['advertise_routes']['speaker_id']
|
||||
LOG.debug('Received routes advertisement end notification '
|
||||
'for speaker_id=%(speaker_id)s from the neutron server.',
|
||||
{'speaker_id': bgp_speaker_id})
|
||||
routes = payload['advertise_routes']['routes']
|
||||
self.add_routes_helper(bgp_speaker_id, routes)
|
||||
|
||||
@utils.synchronized('bgp-dr-agent')
|
||||
def bgp_routes_withdrawal_end(self, context, payload):
|
||||
"""Handle bgp_routes_withdrawal_end notification event."""
|
||||
|
||||
bgp_speaker_id = payload['withdraw_routes']['speaker_id']
|
||||
LOG.debug('Received route withdrawal notification for '
|
||||
'speaker_id=%(speaker_id)s from the neutron server.',
|
||||
{'speaker_id': bgp_speaker_id})
|
||||
routes = payload['withdraw_routes']['routes']
|
||||
self.withdraw_routes_helper(bgp_speaker_id, routes)
|
||||
|
||||
def add_bgp_speaker_helper(self, bgp_speaker_id):
|
||||
"""Add BGP speaker."""
|
||||
bgp_speaker = self.safe_get_bgp_speaker_info(bgp_speaker_id)
|
||||
if bgp_speaker:
|
||||
self.add_bgp_speaker_on_dragent(bgp_speaker)
|
||||
|
||||
def add_bgp_peer_helper(self, bgp_speaker_id, bgp_peer_id):
|
||||
"""Add BGP peer."""
|
||||
# Ideally BGP Speaker must be added by now, If not then let's
|
||||
# re-sync.
|
||||
if not self.cache.is_bgp_speaker_added(bgp_speaker_id):
|
||||
self.schedule_resync(speaker_id=bgp_speaker_id,
|
||||
reason="BGP Speaker Out-of-sync")
|
||||
return
|
||||
|
||||
bgp_peer = self.safe_get_bgp_peer_info(bgp_speaker_id,
|
||||
bgp_peer_id)
|
||||
if bgp_peer:
|
||||
bgp_speaker_as = self.cache.get_bgp_speaker_local_as(
|
||||
bgp_speaker_id)
|
||||
self.add_bgp_peer_to_bgp_speaker(bgp_speaker_id,
|
||||
bgp_speaker_as,
|
||||
bgp_peer)
|
||||
|
||||
def add_routes_helper(self, bgp_speaker_id, routes):
|
||||
"""Advertise routes to BGP speaker."""
|
||||
# Ideally BGP Speaker must be added by now, If not then let's
|
||||
# re-sync.
|
||||
if not self.cache.is_bgp_speaker_added(bgp_speaker_id):
|
||||
self.schedule_resync(speaker_id=bgp_speaker_id,
|
||||
reason="BGP Speaker Out-of-sync")
|
||||
return
|
||||
|
||||
bgp_speaker_as = self.cache.get_bgp_speaker_local_as(bgp_speaker_id)
|
||||
for route in routes:
|
||||
self.advertise_route_via_bgp_speaker(bgp_speaker_id,
|
||||
bgp_speaker_as,
|
||||
route)
|
||||
if self.is_resync_scheduled(bgp_speaker_id):
|
||||
break
|
||||
|
||||
def withdraw_routes_helper(self, bgp_speaker_id, routes):
|
||||
"""Withdraw routes advertised by BGP speaker."""
|
||||
# Ideally BGP Speaker must be added by now, If not then let's
|
||||
# re-sync.
|
||||
if not self.cache.is_bgp_speaker_added(bgp_speaker_id):
|
||||
self.schedule_resync(speaker_id=bgp_speaker_id,
|
||||
reason="BGP Speaker Out-of-sync")
|
||||
return
|
||||
|
||||
bgp_speaker_as = self.cache.get_bgp_speaker_local_as(bgp_speaker_id)
|
||||
for route in routes:
|
||||
self.withdraw_route_via_bgp_speaker(bgp_speaker_id,
|
||||
bgp_speaker_as,
|
||||
route)
|
||||
if self.is_resync_scheduled(bgp_speaker_id):
|
||||
break
|
||||
|
||||
def safe_get_bgp_speaker_info(self, bgp_speaker_id):
|
||||
try:
|
||||
bgp_speaker = self.plugin_rpc.get_bgp_speaker_info(self.context,
|
||||
bgp_speaker_id)
|
||||
if not bgp_speaker:
|
||||
LOG.warning(_LW('BGP Speaker %s has been deleted.'),
|
||||
bgp_speaker_id)
|
||||
return bgp_speaker
|
||||
except Exception as e:
|
||||
self.schedule_resync(speaker_id=bgp_speaker_id,
|
||||
reason=e)
|
||||
LOG.error(_LE('BGP Speaker %(bgp_speaker)s info call '
|
||||
'failed with reason=%(e)s.'),
|
||||
{'bgp_speaker': bgp_speaker_id, 'e': e})
|
||||
|
||||
def safe_get_bgp_peer_info(self, bgp_speaker_id, bgp_peer_id):
|
||||
try:
|
||||
bgp_peer = self.plugin_rpc.get_bgp_peer_info(self.context,
|
||||
bgp_peer_id)
|
||||
if not bgp_peer:
|
||||
LOG.warning(_LW('BGP Peer %s has been deleted.'), bgp_peer)
|
||||
return bgp_peer
|
||||
except Exception as e:
|
||||
self.schedule_resync(speaker_id=bgp_speaker_id,
|
||||
reason=e)
|
||||
LOG.error(_LE('BGP peer %(bgp_peer)s info call '
|
||||
'failed with reason=%(e)s.'),
|
||||
{'bgp_peer': bgp_peer_id, 'e': e})
|
||||
|
||||
@utils.exception_logger()
|
||||
def safe_configure_dragent_for_bgp_speaker(self, bgp_speaker):
|
||||
try:
|
||||
self.add_bgp_speaker_on_dragent(bgp_speaker)
|
||||
except (bgp_ext.BgpSpeakerNotFound, RuntimeError):
|
||||
LOG.warning(_LW('BGP speaker %s may have been deleted and its '
|
||||
'resources may have already been disposed.'),
|
||||
bgp_speaker['id'])
|
||||
|
||||
def add_bgp_speaker_on_dragent(self, bgp_speaker):
|
||||
# Caching BGP speaker details in BGPSpeakerCache. Will be used
|
||||
# during smooth.
|
||||
self.cache.put_bgp_speaker(bgp_speaker)
|
||||
|
||||
LOG.debug('Calling driver for adding BGP speaker %(speaker_id)s,'
|
||||
' speaking for local_as %(local_as)s',
|
||||
{'speaker_id': bgp_speaker['id'],
|
||||
'local_as': bgp_speaker['local_as']})
|
||||
try:
|
||||
self.dr_driver_cls.add_bgp_speaker(bgp_speaker['local_as'])
|
||||
except driver_exc.BgpSpeakerAlreadyScheduled:
|
||||
return
|
||||
except Exception as e:
|
||||
self._handle_driver_failure(bgp_speaker['id'],
|
||||
'add_bgp_speaker', e)
|
||||
|
||||
# Add peer and route information to the driver.
|
||||
self.add_bgp_peers_to_bgp_speaker(bgp_speaker)
|
||||
self.advertise_routes_via_bgp_speaker(bgp_speaker)
|
||||
self.schedule_resync(speaker_id=bgp_speaker['id'],
|
||||
reason="Periodic route cache refresh")
|
||||
|
||||
def remove_bgp_speaker_from_dragent(self, bgp_speaker_id):
|
||||
if self.cache.is_bgp_speaker_added(bgp_speaker_id):
|
||||
bgp_speaker_as = self.cache.get_bgp_speaker_local_as(
|
||||
bgp_speaker_id)
|
||||
self.cache.remove_bgp_speaker_by_id(bgp_speaker_id)
|
||||
|
||||
LOG.debug('Calling driver for removing BGP speaker %(speaker_as)s',
|
||||
{'speaker_as': bgp_speaker_as})
|
||||
try:
|
||||
self.dr_driver_cls.delete_bgp_speaker(bgp_speaker_as)
|
||||
except Exception as e:
|
||||
self._handle_driver_failure(bgp_speaker_id,
|
||||
'remove_bgp_speaker', e)
|
||||
return
|
||||
|
||||
# Ideally, only the added speakers can be removed by the neutron
|
||||
# server. Looks like there might be some synchronization
|
||||
# issue between the server and the agent. Let's initiate a re-sync
|
||||
# to resolve the issue.
|
||||
self.schedule_resync(speaker_id=bgp_speaker_id,
|
||||
reason="BGP Speaker Out-of-sync")
|
||||
|
||||
def add_bgp_peers_to_bgp_speaker(self, bgp_speaker):
|
||||
for bgp_peer in bgp_speaker['peers']:
|
||||
self.add_bgp_peer_to_bgp_speaker(bgp_speaker['id'],
|
||||
bgp_speaker['local_as'],
|
||||
bgp_peer)
|
||||
if self.is_resync_scheduled(bgp_speaker['id']):
|
||||
break
|
||||
|
||||
def add_bgp_peer_to_bgp_speaker(self, bgp_speaker_id,
|
||||
bgp_speaker_as, bgp_peer):
|
||||
if self.cache.get_bgp_peer_by_ip(bgp_speaker_id, bgp_peer['peer_ip']):
|
||||
return
|
||||
|
||||
self.cache.put_bgp_peer(bgp_speaker_id, bgp_peer)
|
||||
|
||||
LOG.debug('Calling driver interface for adding BGP peer %(peer_ip)s '
|
||||
'remote_as=%(remote_as)s to BGP Speaker running for '
|
||||
'local_as=%(local_as)d',
|
||||
{'peer_ip': bgp_peer['peer_ip'],
|
||||
'remote_as': bgp_peer['remote_as'],
|
||||
'local_as': bgp_speaker_as})
|
||||
try:
|
||||
self.dr_driver_cls.add_bgp_peer(bgp_speaker_as,
|
||||
bgp_peer['peer_ip'],
|
||||
bgp_peer['remote_as'],
|
||||
bgp_peer['auth_type'],
|
||||
bgp_peer['password'])
|
||||
except Exception as e:
|
||||
self._handle_driver_failure(bgp_speaker_id,
|
||||
'add_bgp_peer', e)
|
||||
|
||||
def remove_bgp_peer_from_bgp_speaker(self, bgp_speaker_id, bgp_peer_ip):
|
||||
# Ideally BGP Speaker must be added by now, If not then let's
|
||||
# re-sync.
|
||||
if not self.cache.is_bgp_speaker_added(bgp_speaker_id):
|
||||
self.schedule_resync(speaker_id=bgp_speaker_id,
|
||||
reason="BGP Speaker Out-of-sync")
|
||||
return
|
||||
|
||||
if self.cache.is_bgp_peer_added(bgp_speaker_id, bgp_peer_ip):
|
||||
self.cache.remove_bgp_peer_by_ip(bgp_speaker_id, bgp_peer_ip)
|
||||
|
||||
bgp_speaker_as = self.cache.get_bgp_speaker_local_as(
|
||||
bgp_speaker_id)
|
||||
|
||||
LOG.debug('Calling driver interface to remove BGP peer '
|
||||
'%(peer_ip)s from BGP Speaker running for '
|
||||
'local_as=%(local_as)d',
|
||||
{'peer_ip': bgp_peer_ip, 'local_as': bgp_speaker_as})
|
||||
try:
|
||||
self.dr_driver_cls.delete_bgp_peer(bgp_speaker_as,
|
||||
bgp_peer_ip)
|
||||
except Exception as e:
|
||||
self._handle_driver_failure(bgp_speaker_id,
|
||||
'remove_bgp_peer', e)
|
||||
return
|
||||
|
||||
# Ideally, only the added peers can be removed by the neutron
|
||||
# server. Looks like there might be some synchronization
|
||||
# issue between the server and the agent. Let's initiate a re-sync
|
||||
# to resolve the issue.
|
||||
self.schedule_resync(speaker_id=bgp_speaker_id,
|
||||
reason="BGP Peer Out-of-sync")
|
||||
|
||||
def advertise_routes_via_bgp_speaker(self, bgp_speaker):
|
||||