-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[bgpcfgd] Add bgpcfgd support to advertise routes #9197
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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)) | ||
| 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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.