neutron-dynamic-routing/neutron_dynamic_routing/privileged/driver.py

185 lines
6.8 KiB
Python

# 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 ctypes
from ctypes import util as ctypes_util
from multiprocessing import Process
import socket
import eventlet
import netaddr
from os_ken.lib import hub
from os_ken.lib import rpc
from os_ken.services.protocols.bgp.api.base import PREFIX
from os_ken.services.protocols.bgp import bgpspeaker
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
from neutron_dynamic_routing import privileged
from neutron_dynamic_routing.privileged import utils as bgp_utils
from neutron_dynamic_routing.services.bgp.agent.driver import utils
eventlet.monkey_patch()
libc = ctypes.PyDLL(ctypes_util.find_library('c'), use_errno=True)
_setns = libc.setns
CLONE_NEWNET = 0x40000000
LOG = logging.getLogger(__name__)
PROCESS_CACHE = bgp_utils.BgpSpeakerProcessCache()
VERSION_IPV6 = 6
RPC_PORT = 50002
RPC_HOST = '127.0.0.1'
def setns(fd, nstype):
if hasattr(fd, 'fileno'):
fd = fd.fileno()
_setns(fd, nstype)
def get_netns_path(nsname):
return '/var/run/netns/%s' % nsname
@privileged.bgp_speaker_cmd.entrypoint
def add_bgp_speaker(bgp_speaker_id, local_as, bgp_router_id, ns, ip_version):
with open(get_netns_path(ns)) as fd:
setns(fd, CLONE_NEWNET)
bgp_process = BgpProcess(ns, local_as, bgp_router_id, ip_version)
bgp_process.start()
PROCESS_CACHE.put_bgp_speaker_process(bgp_speaker_id, bgp_process)
@privileged.bgp_speaker_cmd.entrypoint
def del_bgp_speaker(bgp_speaker_id, ns):
with open(get_netns_path(ns)) as fd:
setns(fd, CLONE_NEWNET)
endpoint = socket.create_connection((RPC_HOST, RPC_PORT))
client = rpc.Client(endpoint)
client.call('core.stop', [])
process = PROCESS_CACHE.remove_bgp_speaker_process(bgp_speaker_id)
if process:
process.terminate()
@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:
setns(fd, CLONE_NEWNET)
bgp_neighbor = {
neighbors.IP_ADDRESS: peer_ip,
neighbors.REMOTE_AS: remote_as,
PASSWORD: password,
}
endpoint = socket.create_connection((RPC_HOST, RPC_PORT))
client = rpc.Client(endpoint)
client.call('neighbor.create', [bgp_neighbor])
@privileged.bgp_speaker_cmd.entrypoint
def del_bgp_neighbor(bgp_speaker_id, peer_ip, ns):
LOG.info(_LI('BGPAAS: Router namespace= %(ns)s.'), {'ns': ns})
with open(get_netns_path(ns)) as fd:
setns(fd, CLONE_NEWNET)
bgp_neighbor = {
neighbors.IP_ADDRESS: peer_ip,
}
endpoint = socket.create_connection((RPC_HOST, RPC_PORT))
client = rpc.Client(endpoint)
client.call('neighbor.delete', [bgp_neighbor])
@privileged.bgp_speaker_cmd.entrypoint
def advertise_routes(routes):
endpoint = socket.create_connection((RPC_HOST, RPC_PORT))
client = rpc.Client(endpoint)
for route in routes:
networks = {
PREFIX: route,
}
client.call('network.add', [networks])
@privileged.bgp_speaker_cmd.entrypoint
def withdraw_routes(routes):
endpoint = socket.create_connection((RPC_HOST, RPC_PORT))
client = rpc.Client(endpoint)
for route in routes:
networks = {
PREFIX: route,
}
client.call('network.del', [networks])
class BgpProcess(Process):
def __init__(self, namespace, local_as, bgp_router_id, ip_version):
Process.__init__(self)
self._namespace = namespace
self._local_as = local_as
self._bgp_router_id = bgp_router_id
self._ip_version = ip_version
def run(self):
utils.validate_as_num('local_as', self._local_as)
server_host = ('0.0.0.0',)
if self._ip_version == VERSION_IPV6:
server_host = ('::',)
bgpspeaker.BGPSpeaker(
as_number=self._local_as,
router_id=self._bgp_router_id,
bgp_server_hosts=server_host,
best_path_change_handler=self.best_path_change_event,
peer_down_handler=self.bgp_peer_down_event,
peer_up_handler=self.bgp_peer_up_event)
hub.spawn(net_ctrl.NET_CONTROLLER.start,
**{net_ctrl.NC_RPC_BIND_IP: '0.0.0.0',
net_ctrl.NC_RPC_BIND_PORT: RPC_PORT}).wait()
def best_path_change_event(self, event):
LOG.info(_LI("Best path change observed. cidr=%(prefix)s, "
"nexthop=%(nexthop)s, remote_as=%(remote_as)d, "
"is_withdraw=%(is_withdraw)s, "
"namespace=%(namespace)s"),
{'prefix': event.prefix, 'nexthop': event.nexthop,
'remote_as': event.remote_as,
'is_withdraw': event.is_withdraw,
'namespace': self._namespace})
ip_version = netaddr.IPNetwork(event.prefix or event.nexthop).version
if event.is_withdraw:
ip_lib.delete_ip_route(self._namespace, event.prefix,
ip_version, via=event.nexthop)
else:
ip_lib.add_ip_route(self._namespace, event.prefix, ip_version,
via=event.nexthop)
# Function for logging BGP peer.
def bgp_peer_down_event(self, remote_ip, remote_as):
LOG.info(_LI('BGP Peer %(peer_ip)s for remote_as=%(peer_as)d went '
'DOWN. namespace=%(namespace)s'),
{'peer_ip': remote_ip, 'peer_as': remote_as,
'namespace': self._namespace})
def bgp_peer_up_event(self, remote_ip, remote_as):
LOG.info(_LI('BGP Peer %(peer_ip)s for remote_as=%(peer_as)d is UP.'
'namespace=%(namespace)s'),
{'peer_ip': remote_ip, 'peer_as': remote_as,
'namespace': self._namespace})