Skip to content
Merged
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 files/scripts/swss.sh
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ start() {
$SONIC_DB_CLI GB_ASIC_DB FLUSHDB
$SONIC_DB_CLI GB_COUNTERS_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*'"
$SONIC_DB_CLI APPL_STATE_DB FLUSHDB
fi

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 @@ -59,6 +60,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):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does the set handler invoked when bgp is restarted? please confirm

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked the configuration after bgp restart and it would be invoked after bgp restart.

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):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may also want to add the new table to cleanup script to avoid stale entries in swss restarts

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the table in the script you suggested to clean up in swss restart.

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