Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bgpcfgd] Add bgpcfgd support to advertise routes (#9197) #22

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
url = https://github.com/Azure/sonic-sairedis
[submodule "sonic-swss"]
path = src/sonic-swss
url = https://github.com/Azure/sonic-swss
url = https://github.com/dgsudharsan/sonic-swss
[submodule "src/p4c-bm/p4c-bm"]
path = platform/p4/p4c-bm/p4c-bm
url = https://github.com/krambn/p4c-bm
Expand Down
2 changes: 1 addition & 1 deletion files/scripts/swss.sh
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ start() {
$SONIC_DB_CLI COUNTERS_DB FLUSHDB
$SONIC_DB_CLI FLEX_COUNTER_DB FLUSHDB
$SONIC_DB_CLI RESTAPI_DB FLUSHDB
clean_up_tables STATE_DB "'PORT_TABLE*', 'MGMT_PORT_TABLE*', 'VLAN_TABLE*', 'VLAN_MEMBER_TABLE*', 'LAG_TABLE*', 'LAG_MEMBER_TABLE*', 'INTERFACE_TABLE*', 'MIRROR_SESSION*', 'VRF_TABLE*', 'FDB_TABLE*', 'FG_ROUTE_TABLE*', 'BUFFER_POOL*', 'BUFFER_PROFILE*', 'MUX_CABLE_TABLE*'"
clean_up_tables STATE_DB "'PORT_TABLE*', 'MGMT_PORT_TABLE*', 'VLAN_TABLE*', 'VLAN_MEMBER_TABLE*', 'LAG_TABLE*', 'LAG_MEMBER_TABLE*', 'INTERFACE_TABLE*', 'MIRROR_SESSION*', 'VRF_TABLE*', 'FDB_TABLE*', 'FG_ROUTE_TABLE*', 'BUFFER_POOL*', 'BUFFER_PROFILE*', 'MUX_CABLE_TABLE*', 'ADVERTISE_NETWORK_TABLE*'"
fi

# start service docker
Expand Down
3 changes: 3 additions & 0 deletions src/sonic-bgpcfgd/bgpcfgd/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .config import ConfigMgr
from .directory import Directory
from .log import log_notice, log_crit
from .managers_advertise_rt import AdvertiseRouteMgr
from .managers_allow_list import BGPAllowListMgr
from .managers_bbr import BBRMgr
from .managers_bgp import BGPPeerMgrBase
Expand Down Expand Up @@ -57,6 +58,8 @@ def do_work():
BBRMgr(common_objs, "CONFIG_DB", "BGP_BBR"),
# Static Route Managers
StaticRouteMgr(common_objs, "CONFIG_DB", "STATIC_ROUTE"),
# Route Advertisement Managers
AdvertiseRouteMgr(common_objs, "STATE_DB", swsscommon.STATE_ADVERTISE_NETWORK_TABLE_NAME),
]
runner = Runner(common_objs['cfg_mgr'])
for mgr in managers:
Expand Down
108 changes: 108 additions & 0 deletions src/sonic-bgpcfgd/bgpcfgd/managers_advertise_rt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
from .manager import Manager
from .template import TemplateFabric
from swsscommon import swsscommon


class AdvertiseRouteMgr(Manager):
""" This class Advertises routes when ADVERTISE_NETWORK_TABLE in STATE_DB is updated """
def __init__(self, common_objs, db, table):
"""
Initialize the object
:param common_objs: common object dictionary
:param db: name of the db
:param table: name of the table in the db
"""
super(AdvertiseRouteMgr, self).__init__(
common_objs,
[],
db,
table,
)

self.directory.subscribe([("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost/bgp_asn"),], self.on_bgp_asn_change)
self.advertised_routes = dict()


OP_DELETE = 'DELETE'
OP_ADD = 'ADD'


def set_handler(self, key, data):
vrf, ip_prefix = self.split_key(key)
self.add_route_advertisement(vrf, ip_prefix)

return True


def del_handler(self, key):
vrf, ip_prefix = self.split_key(key)
self.remove_route_advertisement(vrf, ip_prefix)


def add_route_advertisement(self, vrf, ip_prefix):
if self.directory.path_exist("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost/bgp_asn"):
if not self.advertised_routes.get(vrf, set()):
self.bgp_network_import_check_commands(vrf, self.OP_ADD)
self.advertise_route_commands(ip_prefix, vrf, self.OP_ADD)

self.advertised_routes.setdefault(vrf, set()).add(ip_prefix)


def remove_route_advertisement(self, vrf, ip_prefix):
self.advertised_routes.setdefault(vrf, set()).discard(ip_prefix)
if not self.advertised_routes.get(vrf, set()):
self.advertised_routes.pop(vrf, None)

if self.directory.path_exist("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost/bgp_asn"):
if not self.advertised_routes.get(vrf, set()):
self.bgp_network_import_check_commands(vrf, self.OP_DELETE)
self.advertise_route_commands(ip_prefix, vrf, self.OP_DELETE)


def advertise_route_commands(self, ip_prefix, vrf, op):
is_ipv6 = TemplateFabric.is_ipv6(ip_prefix)
bgp_asn = self.directory.get_slot("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME)["localhost"]["bgp_asn"]

cmd_list = []
if vrf == 'default':
cmd_list.append("router bgp %s" % bgp_asn)
else:
cmd_list.append("router bgp %s vrf %s" % (bgp_asn, vrf))

cmd_list.append(" address-family %s unicast" % ("ipv6" if is_ipv6 else "ipv4"))
cmd_list.append(" %snetwork %s" % ('no ' if op == self.OP_DELETE else '', ip_prefix))

self.cfg_mgr.push_list(cmd_list)


def bgp_network_import_check_commands(self, vrf, op):
bgp_asn = self.directory.get_slot("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME)["localhost"]["bgp_asn"]
cmd_list = []
if vrf == 'default':
cmd_list.append("router bgp %s" % bgp_asn)
else:
cmd_list.append("router bgp %s vrf %s" % (bgp_asn, vrf))
cmd_list.append(" %sbgp network import-check" % ('' if op == self.OP_DELETE else 'no '))

self.cfg_mgr.push_list(cmd_list)


def on_bgp_asn_change(self):
if self.directory.path_exist("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost/bgp_asn"):
for vrf, ip_prefixes in self.advertised_routes.items():
self.bgp_network_import_check_commands(vrf, self.OP_ADD)
for ip_prefix in ip_prefixes:
self.add_route_advertisement(vrf, ip_prefix)


@staticmethod
def split_key(key):
"""
Split key into vrf name and prefix.
:param key: key to split
:return: vrf name extracted from the key, ip prefix extracted from the key
"""
if '|' not in key:
return 'default', key
else:
return tuple(key.split('|', 1))
185 changes: 185 additions & 0 deletions src/sonic-bgpcfgd/tests/test_advertise_rt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
from unittest.mock import MagicMock, patch

from bgpcfgd.directory import Directory
from bgpcfgd.template import TemplateFabric
from bgpcfgd.managers_advertise_rt import AdvertiseRouteMgr
from swsscommon import swsscommon

def constructor(skip_bgp_asn=False):
cfg_mgr = MagicMock()

common_objs = {
'directory': Directory(),
'cfg_mgr': cfg_mgr,
'tf': TemplateFabric(),
'constants': {},
}

mgr = AdvertiseRouteMgr(common_objs, "STATE_DB", swsscommon.STATE_ADVERTISE_NETWORK_TABLE_NAME)
if not skip_bgp_asn:
mgr.directory.put("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost", {"bgp_asn": "65100"})
assert len(mgr.advertised_routes) == 0

return mgr

def set_del_test(mgr, op, args, expected_ret, expected_cmds):
set_del_test.push_list_called = False
def push_list(cmds):
set_del_test.push_list_called = True
assert cmds in expected_cmds
return True
mgr.cfg_mgr.push_list = push_list

if op == "SET":
ret = mgr.set_handler(*args)
assert ret == expected_ret
elif op == "DEL":
mgr.del_handler(*args)
else:
assert False, "Wrong operation"

if expected_cmds:
assert set_del_test.push_list_called, "cfg_mgr.push_list wasn't called"
else:
assert not set_del_test.push_list_called, "cfg_mgr.push_list was called"

def test_set_del():
mgr = constructor()
set_del_test(
mgr,
"SET",
("10.1.0.0/24", {}),
True,
[
["router bgp 65100",
" no bgp network import-check"],
["router bgp 65100",
" address-family ipv4 unicast",
" network 10.1.0.0/24"]
]
)

set_del_test(
mgr,
"SET",
("fc00:10::/64", {}),
True,
[
["router bgp 65100",
" address-family ipv6 unicast",
" network fc00:10::/64"]
]
)

set_del_test(
mgr,
"DEL",
("10.1.0.0/24",),
True,
[
["router bgp 65100",
" address-family ipv4 unicast",
" no network 10.1.0.0/24"]
]
)

set_del_test(
mgr,
"DEL",
("fc00:10::/64",),
True,
[
["router bgp 65100",
" bgp network import-check"],
["router bgp 65100",
" address-family ipv6 unicast",
" no network fc00:10::/64"]
]
)


def test_set_del_vrf():
mgr = constructor()
set_del_test(
mgr,
"SET",
("vrfRED|10.2.0.0/24", {}),
True,
[
["router bgp 65100 vrf vrfRED",
" no bgp network import-check"],
["router bgp 65100 vrf vrfRED",
" address-family ipv4 unicast",
" network 10.2.0.0/24"]
]
)

set_del_test(
mgr,
"SET",
("vrfRED|fc00:20::/64", {}),
True,
[
["router bgp 65100 vrf vrfRED",
" address-family ipv6 unicast",
" network fc00:20::/64"]
]
)

set_del_test(
mgr,
"DEL",
("vrfRED|10.2.0.0/24",),
True,
[
["router bgp 65100 vrf vrfRED",
" address-family ipv4 unicast",
" no network 10.2.0.0/24"]
]
)

set_del_test(
mgr,
"DEL",
("vrfRED|fc00:20::/64",),
True,
[
["router bgp 65100 vrf vrfRED",
" bgp network import-check"],
["router bgp 65100 vrf vrfRED",
" address-family ipv6 unicast",
" no network fc00:20::/64"]
]
)


def test_set_del_bgp_asn_change():
mgr = constructor(skip_bgp_asn=True)
set_del_test(
mgr,
"SET",
("vrfRED|10.3.0.0/24", {}),
True,
[]
)


test_set_del_bgp_asn_change.push_list_called = False
expected_cmds = [
["router bgp 65100 vrf vrfRED",
" no bgp network import-check"],
["router bgp 65100 vrf vrfRED",
" address-family ipv4 unicast",
" network 10.3.0.0/24"]
]
def push_list(cmds):
test_set_del_bgp_asn_change.push_list_called = True
assert cmds in expected_cmds
return True

mgr.cfg_mgr.push_list = push_list
assert not test_set_del_bgp_asn_change.push_list_called

mgr.directory.put("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost", {"bgp_asn": "65100"})

assert test_set_del_bgp_asn_change.push_list_called