207 lines
9.4 KiB
Python
207 lines
9.4 KiB
Python
# 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 os_ken.services.protocols.bgp import bgpspeaker
|
|
from os_ken.services.protocols.bgp.rtconf.neighbors import CONNECT_MODE_ACTIVE
|
|
from oslo_log import log as logging
|
|
from oslo_utils import encodeutils
|
|
|
|
from neutron_dynamic_routing._i18n import _LE, _LI
|
|
from neutron_dynamic_routing.services.bgp.agent.driver import base
|
|
from neutron_dynamic_routing.services.bgp.agent.driver import exceptions as bgp_driver_exc # noqa
|
|
from neutron_dynamic_routing.services.bgp.agent.driver import utils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
# Function for logging BGP peer and path changes.
|
|
def bgp_peer_down_cb(remote_ip, remote_as):
|
|
LOG.info(_LI('BGP Peer %(peer_ip)s for remote_as=%(peer_as)d went DOWN.'),
|
|
{'peer_ip': remote_ip, 'peer_as': remote_as})
|
|
|
|
|
|
def bgp_peer_up_cb(remote_ip, remote_as):
|
|
LOG.info(_LI('BGP Peer %(peer_ip)s for remote_as=%(peer_as)d is UP.'),
|
|
{'peer_ip': remote_ip, 'peer_as': remote_as})
|
|
|
|
|
|
def best_path_change_cb(event):
|
|
LOG.info(_LI("Best path change observed. cidr=%(prefix)s, "
|
|
"nexthop=%(nexthop)s, remote_as=%(remote_as)d, "
|
|
"is_withdraw=%(is_withdraw)s"),
|
|
{'prefix': event.prefix, 'nexthop': event.nexthop,
|
|
'remote_as': event.remote_as,
|
|
'is_withdraw': event.is_withdraw})
|
|
|
|
|
|
class OsKenBgpDriver(base.BgpDriverBase):
|
|
"""BGP speaker implementation via os-ken."""
|
|
|
|
def __init__(self, cfg):
|
|
LOG.info(_LI('Initializing os-ken driver for BGP functionality.'))
|
|
self._read_config(cfg)
|
|
|
|
# Note: Even though os-ken can only support one BGP speaker as of now,
|
|
# we have tried making the framework generic for the future purposes.
|
|
self.cache = utils.BgpMultiSpeakerCache()
|
|
|
|
def _read_config(self, cfg):
|
|
if cfg is None or cfg.bgp_router_id is None:
|
|
# If either cfg or router_id is not specified, raise voice
|
|
LOG.error(_LE('BGP router-id MUST be specified for the correct '
|
|
'functional working.'))
|
|
else:
|
|
self.routerid = cfg.bgp_router_id
|
|
LOG.info(_LI('Initialized os-ken BGP Speaker driver interface '
|
|
'with bgp_router_id=%s'), self.routerid)
|
|
|
|
def add_bgp_speaker(self, speaker_as):
|
|
curr_speaker = self.cache.get_bgp_speaker(speaker_as)
|
|
if curr_speaker is not None:
|
|
raise bgp_driver_exc.BgpSpeakerAlreadyScheduled(
|
|
current_as=speaker_as,
|
|
rtid=self.routerid)
|
|
|
|
# os-ken can only support One speaker
|
|
if self.cache.get_hosted_bgp_speakers_count() == 1:
|
|
raise bgp_driver_exc.BgpSpeakerMaxScheduled(count=1)
|
|
|
|
# Validate input parameters.
|
|
# speaker_as must be an integer in the allowed range.
|
|
utils.validate_as_num('local_as', speaker_as)
|
|
|
|
# Notify os-ken about BGP Speaker addition.
|
|
# Please note: Since, only the route-advertisement support is
|
|
# implemented we are explicitly setting the bgp_server_port
|
|
# attribute to 0 which disables listening on port 179.
|
|
curr_speaker = bgpspeaker.BGPSpeaker(as_number=speaker_as,
|
|
router_id=self.routerid, bgp_server_port=0,
|
|
best_path_change_handler=best_path_change_cb,
|
|
peer_down_handler=bgp_peer_down_cb,
|
|
peer_up_handler=bgp_peer_up_cb)
|
|
LOG.info(_LI('Added BGP Speaker for local_as=%(as)d with '
|
|
'router_id= %(rtid)s.'),
|
|
{'as': speaker_as, 'rtid': self.routerid})
|
|
|
|
self.cache.put_bgp_speaker(speaker_as, curr_speaker)
|
|
|
|
def delete_bgp_speaker(self, speaker_as):
|
|
curr_speaker = self.cache.get_bgp_speaker(speaker_as)
|
|
if not curr_speaker:
|
|
raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as,
|
|
rtid=self.routerid)
|
|
# Notify os-ken about BGP Speaker deletion
|
|
curr_speaker.shutdown()
|
|
LOG.info(_LI('Removed BGP Speaker for local_as=%(as)d with '
|
|
'router_id=%(rtid)s.'),
|
|
{'as': speaker_as, 'rtid': self.routerid})
|
|
self.cache.remove_bgp_speaker(speaker_as)
|
|
|
|
def add_bgp_peer(self, speaker_as, peer_ip, peer_as,
|
|
auth_type='none', password=None):
|
|
curr_speaker = self.cache.get_bgp_speaker(speaker_as)
|
|
if not curr_speaker:
|
|
raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as,
|
|
rtid=self.routerid)
|
|
|
|
# Validate peer_ip and peer_as.
|
|
utils.validate_as_num('remote_as', peer_as)
|
|
utils.validate_ip_addr(peer_ip)
|
|
utils.validate_auth(auth_type, password)
|
|
if password is not None:
|
|
password = encodeutils.to_utf8(password)
|
|
|
|
curr_speaker.neighbor_add(address=peer_ip,
|
|
remote_as=peer_as,
|
|
enable_ipv4=True,
|
|
enable_ipv6=True,
|
|
password=password,
|
|
connect_mode=CONNECT_MODE_ACTIVE)
|
|
LOG.info(_LI('Added BGP Peer %(peer)s for remote_as=%(as)d to '
|
|
'BGP Speaker running for local_as=%(local_as)d.'),
|
|
{'peer': peer_ip, 'as': peer_as, 'local_as': speaker_as})
|
|
|
|
def delete_bgp_peer(self, speaker_as, peer_ip):
|
|
curr_speaker = self.cache.get_bgp_speaker(speaker_as)
|
|
if not curr_speaker:
|
|
raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as,
|
|
rtid=self.routerid)
|
|
# Validate peer_ip. It must be a string.
|
|
utils.validate_ip_addr(peer_ip)
|
|
|
|
# Notify os-ken about BGP Peer removal
|
|
curr_speaker.neighbor_del(address=peer_ip)
|
|
LOG.info(_LI('Removed BGP Peer %(peer)s from BGP Speaker '
|
|
'running for local_as=%(local_as)d.'),
|
|
{'peer': peer_ip, 'local_as': speaker_as})
|
|
|
|
def advertise_route(self, speaker_as, cidr, nexthop):
|
|
curr_speaker = self.cache.get_bgp_speaker(speaker_as)
|
|
if not curr_speaker:
|
|
raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as,
|
|
rtid=self.routerid)
|
|
|
|
# Validate cidr and nexthop. Both must be strings.
|
|
utils.validate_string(cidr)
|
|
utils.validate_string(nexthop)
|
|
|
|
# Notify os-ken about route advertisement
|
|
curr_speaker.prefix_add(prefix=cidr, next_hop=nexthop)
|
|
LOG.info(_LI('Route cidr=%(prefix)s, nexthop=%(nexthop)s is '
|
|
'advertised for BGP Speaker running for '
|
|
'local_as=%(local_as)d.'),
|
|
{'prefix': cidr, 'nexthop': nexthop, 'local_as': speaker_as})
|
|
|
|
def withdraw_route(self, speaker_as, cidr, nexthop=None):
|
|
curr_speaker = self.cache.get_bgp_speaker(speaker_as)
|
|
if not curr_speaker:
|
|
raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as,
|
|
rtid=self.routerid)
|
|
# Validate cidr. It must be a string.
|
|
utils.validate_string(cidr)
|
|
|
|
# Notify os-ken about route withdrawal
|
|
curr_speaker.prefix_del(prefix=cidr)
|
|
LOG.info(_LI('Route cidr=%(prefix)s is withdrawn from BGP Speaker '
|
|
'running for local_as=%(local_as)d.'),
|
|
{'prefix': cidr, 'local_as': speaker_as})
|
|
|
|
def get_bgp_speaker_statistics(self, speaker_as):
|
|
LOG.info(_LI('Collecting BGP Speaker statistics for local_as=%d.'),
|
|
speaker_as)
|
|
curr_speaker = self.cache.get_bgp_speaker(speaker_as)
|
|
if not curr_speaker:
|
|
raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as,
|
|
rtid=self.routerid)
|
|
|
|
# TODO(vikram): Filter and return the necessary information.
|
|
# Will be done as part of new RFE requirement
|
|
# https://bugs.launchpad.net/neutron/+bug/1527993
|
|
return curr_speaker.neighbor_state_get()
|
|
|
|
def get_bgp_peer_statistics(self, speaker_as, peer_ip):
|
|
LOG.info(_LI('Collecting BGP Peer statistics for peer_ip=%(peer)s, '
|
|
'running in speaker_as=%(speaker_as)d '),
|
|
{'peer': peer_ip, 'speaker_as': speaker_as})
|
|
curr_speaker = self.cache.get_bgp_speaker(speaker_as)
|
|
if not curr_speaker:
|
|
raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as,
|
|
rtid=self.routerid)
|
|
|
|
# TODO(vikram): Filter and return the necessary information.
|
|
# Will be done as part of new RFE requirement
|
|
# https://bugs.launchpad.net/neutron/+bug/1527993
|
|
return curr_speaker.neighbor_state_get(address=peer_ip)
|