Skip to content

Commit 4191889

Browse files
authored
[bgpcfgd] Add bgpcfgd support to advertise routes (#9197) (#9697)
Why I did it Cherry pick changes in #9197 to 202012 branch Add bgpcfgd support to advertise routes. How I did it Make bgpcfgd subscribe to the ADVERTISE_NETWORK table in STATE_DB and configure route advertisement accordingly. How to verify it Added unit tests in bgpcfgd and verify on KVM about route advertisement.
1 parent e6b22b1 commit 4191889

File tree

4 files changed

+297
-1
lines changed

4 files changed

+297
-1
lines changed

files/scripts/swss.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ start() {
147147
$SONIC_DB_CLI COUNTERS_DB FLUSHDB
148148
$SONIC_DB_CLI FLEX_COUNTER_DB FLUSHDB
149149
$SONIC_DB_CLI RESTAPI_DB FLUSHDB
150-
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*'"
150+
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*'"
151151
fi
152152

153153
# start service docker

src/sonic-bgpcfgd/bgpcfgd/main.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .config import ConfigMgr
1010
from .directory import Directory
1111
from .log import log_notice, log_crit
12+
from .managers_advertise_rt import AdvertiseRouteMgr
1213
from .managers_allow_list import BGPAllowListMgr
1314
from .managers_bbr import BBRMgr
1415
from .managers_bgp import BGPPeerMgrBase
@@ -57,6 +58,8 @@ def do_work():
5758
BBRMgr(common_objs, "CONFIG_DB", "BGP_BBR"),
5859
# Static Route Managers
5960
StaticRouteMgr(common_objs, "CONFIG_DB", "STATIC_ROUTE"),
61+
# Route Advertisement Managers
62+
AdvertiseRouteMgr(common_objs, "STATE_DB", swsscommon.STATE_ADVERTISE_NETWORK_TABLE_NAME),
6063
]
6164
runner = Runner(common_objs['cfg_mgr'])
6265
for mgr in managers:
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
from .manager import Manager
2+
from .template import TemplateFabric
3+
from swsscommon import swsscommon
4+
5+
6+
class AdvertiseRouteMgr(Manager):
7+
""" This class Advertises routes when ADVERTISE_NETWORK_TABLE in STATE_DB is updated """
8+
def __init__(self, common_objs, db, table):
9+
"""
10+
Initialize the object
11+
:param common_objs: common object dictionary
12+
:param db: name of the db
13+
:param table: name of the table in the db
14+
"""
15+
super(AdvertiseRouteMgr, self).__init__(
16+
common_objs,
17+
[],
18+
db,
19+
table,
20+
)
21+
22+
self.directory.subscribe([("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost/bgp_asn"),], self.on_bgp_asn_change)
23+
self.advertised_routes = dict()
24+
25+
26+
OP_DELETE = 'DELETE'
27+
OP_ADD = 'ADD'
28+
29+
30+
def set_handler(self, key, data):
31+
vrf, ip_prefix = self.split_key(key)
32+
self.add_route_advertisement(vrf, ip_prefix)
33+
34+
return True
35+
36+
37+
def del_handler(self, key):
38+
vrf, ip_prefix = self.split_key(key)
39+
self.remove_route_advertisement(vrf, ip_prefix)
40+
41+
42+
def add_route_advertisement(self, vrf, ip_prefix):
43+
if self.directory.path_exist("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost/bgp_asn"):
44+
if not self.advertised_routes.get(vrf, set()):
45+
self.bgp_network_import_check_commands(vrf, self.OP_ADD)
46+
self.advertise_route_commands(ip_prefix, vrf, self.OP_ADD)
47+
48+
self.advertised_routes.setdefault(vrf, set()).add(ip_prefix)
49+
50+
51+
def remove_route_advertisement(self, vrf, ip_prefix):
52+
self.advertised_routes.setdefault(vrf, set()).discard(ip_prefix)
53+
if not self.advertised_routes.get(vrf, set()):
54+
self.advertised_routes.pop(vrf, None)
55+
56+
if self.directory.path_exist("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost/bgp_asn"):
57+
if not self.advertised_routes.get(vrf, set()):
58+
self.bgp_network_import_check_commands(vrf, self.OP_DELETE)
59+
self.advertise_route_commands(ip_prefix, vrf, self.OP_DELETE)
60+
61+
62+
def advertise_route_commands(self, ip_prefix, vrf, op):
63+
is_ipv6 = TemplateFabric.is_ipv6(ip_prefix)
64+
bgp_asn = self.directory.get_slot("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME)["localhost"]["bgp_asn"]
65+
66+
cmd_list = []
67+
if vrf == 'default':
68+
cmd_list.append("router bgp %s" % bgp_asn)
69+
else:
70+
cmd_list.append("router bgp %s vrf %s" % (bgp_asn, vrf))
71+
72+
cmd_list.append(" address-family %s unicast" % ("ipv6" if is_ipv6 else "ipv4"))
73+
cmd_list.append(" %snetwork %s" % ('no ' if op == self.OP_DELETE else '', ip_prefix))
74+
75+
self.cfg_mgr.push_list(cmd_list)
76+
77+
78+
def bgp_network_import_check_commands(self, vrf, op):
79+
bgp_asn = self.directory.get_slot("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME)["localhost"]["bgp_asn"]
80+
cmd_list = []
81+
if vrf == 'default':
82+
cmd_list.append("router bgp %s" % bgp_asn)
83+
else:
84+
cmd_list.append("router bgp %s vrf %s" % (bgp_asn, vrf))
85+
cmd_list.append(" %sbgp network import-check" % ('' if op == self.OP_DELETE else 'no '))
86+
87+
self.cfg_mgr.push_list(cmd_list)
88+
89+
90+
def on_bgp_asn_change(self):
91+
if self.directory.path_exist("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost/bgp_asn"):
92+
for vrf, ip_prefixes in self.advertised_routes.items():
93+
self.bgp_network_import_check_commands(vrf, self.OP_ADD)
94+
for ip_prefix in ip_prefixes:
95+
self.add_route_advertisement(vrf, ip_prefix)
96+
97+
98+
@staticmethod
99+
def split_key(key):
100+
"""
101+
Split key into vrf name and prefix.
102+
:param key: key to split
103+
:return: vrf name extracted from the key, ip prefix extracted from the key
104+
"""
105+
if '|' not in key:
106+
return 'default', key
107+
else:
108+
return tuple(key.split('|', 1))
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
from unittest.mock import MagicMock, patch
2+
3+
from bgpcfgd.directory import Directory
4+
from bgpcfgd.template import TemplateFabric
5+
from bgpcfgd.managers_advertise_rt import AdvertiseRouteMgr
6+
from swsscommon import swsscommon
7+
8+
def constructor(skip_bgp_asn=False):
9+
cfg_mgr = MagicMock()
10+
11+
common_objs = {
12+
'directory': Directory(),
13+
'cfg_mgr': cfg_mgr,
14+
'tf': TemplateFabric(),
15+
'constants': {},
16+
}
17+
18+
mgr = AdvertiseRouteMgr(common_objs, "STATE_DB", swsscommon.STATE_ADVERTISE_NETWORK_TABLE_NAME)
19+
if not skip_bgp_asn:
20+
mgr.directory.put("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost", {"bgp_asn": "65100"})
21+
assert len(mgr.advertised_routes) == 0
22+
23+
return mgr
24+
25+
def set_del_test(mgr, op, args, expected_ret, expected_cmds):
26+
set_del_test.push_list_called = False
27+
def push_list(cmds):
28+
set_del_test.push_list_called = True
29+
assert cmds in expected_cmds
30+
return True
31+
mgr.cfg_mgr.push_list = push_list
32+
33+
if op == "SET":
34+
ret = mgr.set_handler(*args)
35+
assert ret == expected_ret
36+
elif op == "DEL":
37+
mgr.del_handler(*args)
38+
else:
39+
assert False, "Wrong operation"
40+
41+
if expected_cmds:
42+
assert set_del_test.push_list_called, "cfg_mgr.push_list wasn't called"
43+
else:
44+
assert not set_del_test.push_list_called, "cfg_mgr.push_list was called"
45+
46+
def test_set_del():
47+
mgr = constructor()
48+
set_del_test(
49+
mgr,
50+
"SET",
51+
("10.1.0.0/24", {}),
52+
True,
53+
[
54+
["router bgp 65100",
55+
" no bgp network import-check"],
56+
["router bgp 65100",
57+
" address-family ipv4 unicast",
58+
" network 10.1.0.0/24"]
59+
]
60+
)
61+
62+
set_del_test(
63+
mgr,
64+
"SET",
65+
("fc00:10::/64", {}),
66+
True,
67+
[
68+
["router bgp 65100",
69+
" address-family ipv6 unicast",
70+
" network fc00:10::/64"]
71+
]
72+
)
73+
74+
set_del_test(
75+
mgr,
76+
"DEL",
77+
("10.1.0.0/24",),
78+
True,
79+
[
80+
["router bgp 65100",
81+
" address-family ipv4 unicast",
82+
" no network 10.1.0.0/24"]
83+
]
84+
)
85+
86+
set_del_test(
87+
mgr,
88+
"DEL",
89+
("fc00:10::/64",),
90+
True,
91+
[
92+
["router bgp 65100",
93+
" bgp network import-check"],
94+
["router bgp 65100",
95+
" address-family ipv6 unicast",
96+
" no network fc00:10::/64"]
97+
]
98+
)
99+
100+
101+
def test_set_del_vrf():
102+
mgr = constructor()
103+
set_del_test(
104+
mgr,
105+
"SET",
106+
("vrfRED|10.2.0.0/24", {}),
107+
True,
108+
[
109+
["router bgp 65100 vrf vrfRED",
110+
" no bgp network import-check"],
111+
["router bgp 65100 vrf vrfRED",
112+
" address-family ipv4 unicast",
113+
" network 10.2.0.0/24"]
114+
]
115+
)
116+
117+
set_del_test(
118+
mgr,
119+
"SET",
120+
("vrfRED|fc00:20::/64", {}),
121+
True,
122+
[
123+
["router bgp 65100 vrf vrfRED",
124+
" address-family ipv6 unicast",
125+
" network fc00:20::/64"]
126+
]
127+
)
128+
129+
set_del_test(
130+
mgr,
131+
"DEL",
132+
("vrfRED|10.2.0.0/24",),
133+
True,
134+
[
135+
["router bgp 65100 vrf vrfRED",
136+
" address-family ipv4 unicast",
137+
" no network 10.2.0.0/24"]
138+
]
139+
)
140+
141+
set_del_test(
142+
mgr,
143+
"DEL",
144+
("vrfRED|fc00:20::/64",),
145+
True,
146+
[
147+
["router bgp 65100 vrf vrfRED",
148+
" bgp network import-check"],
149+
["router bgp 65100 vrf vrfRED",
150+
" address-family ipv6 unicast",
151+
" no network fc00:20::/64"]
152+
]
153+
)
154+
155+
156+
def test_set_del_bgp_asn_change():
157+
mgr = constructor(skip_bgp_asn=True)
158+
set_del_test(
159+
mgr,
160+
"SET",
161+
("vrfRED|10.3.0.0/24", {}),
162+
True,
163+
[]
164+
)
165+
166+
167+
test_set_del_bgp_asn_change.push_list_called = False
168+
expected_cmds = [
169+
["router bgp 65100 vrf vrfRED",
170+
" no bgp network import-check"],
171+
["router bgp 65100 vrf vrfRED",
172+
" address-family ipv4 unicast",
173+
" network 10.3.0.0/24"]
174+
]
175+
def push_list(cmds):
176+
test_set_del_bgp_asn_change.push_list_called = True
177+
assert cmds in expected_cmds
178+
return True
179+
180+
mgr.cfg_mgr.push_list = push_list
181+
assert not test_set_del_bgp_asn_change.push_list_called
182+
183+
mgr.directory.put("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost", {"bgp_asn": "65100"})
184+
185+
assert test_set_del_bgp_asn_change.push_list_called

0 commit comments

Comments
 (0)