From cf02642028eed09e8690f62828404c1bf7236feb Mon Sep 17 00:00:00 2001 From: BYGX-wcr Date: Mon, 2 Dec 2024 22:28:00 +0000 Subject: [PATCH 1/6] src/sonic-bgpcfgd/bgpcfgd: init draft of srv6 manager --- src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py | 87 ++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py diff --git a/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py b/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py new file mode 100644 index 000000000000..abca1d719c2d --- /dev/null +++ b/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py @@ -0,0 +1,87 @@ +import traceback +from .log import log_crit, log_err, log_debug, log_warn +from .manager import Manager +from .template import TemplateFabric +import socket +from swsscommon import swsscommon +from ipaddress import ip_network, IPv6Network + +class SRv6Mgr(Manager): + """ This class updates SRv6 configurations when SRV6_MY_SID_TABLE table 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(SRv6Mgr, self).__init__( + common_objs, + [], + db, + table, + ) + + self.sids = {} # locators -> SIDs + self.config_db = None + + def set_handler(self, key, data): + ip_addr = key + sid = SID(ip_addr, data) + locator = sid.get_locator() + opcode = sid.get_opcode() + + # TODO: generate FRR commands and push to Config Manager + + self.sids.setdefault(locator, {})[opcode] = sid + + def del_handler(self, key, data): + ip_addr = key + sid = SID(ip_addr, data) + locator = sid.get_locator() + opcode = sid.get_opcode() + + if locator in self.sids: + #TODO: delete FRR opcode config + pass + else: + log_warn("Encountered an unexpected config change") + return + +class SID: + def __init__(self, ip_addr, data): + self.bits = int(IPv6Network(ip_addr).network_address) + self.block_len = data['block_len'] if 'block_len' in data else 32 + self.node_len = data['node_len'] if 'node_len' in data else 16 + self.func_len = data['func_len'] if 'func_len' in data else 16 + self.arg_len = data['arg_len'] if 'arg_len' in data else 0 + + # extract the locator(block id + node id) embedded in the SID + locator_mask = 0 + for i in range(self.block_len + self.node_len): + locator_mask <<= 1 + locator_mask |= 0x01 + locator_mask <<= 128 - (self.block_len + self.node_len) + self.locator = self.bits & locator_mask + + # extract the opcode(function id) + func_mask = 0 + for i in range(self.func_len): + func_mask <<= 1 + func_mask != 0x01 + func_mask <<= 128 - (self.block_len + self.node_len + self.func_len) + self.opcode = self.bits & func_mask + + if 'action' not in data: + log_err("Found a SRv6 config entry that does not specify action: {}|{}".format(ip_addr, data)) + raise RuntimeError("SID creation encountered error!") + + self.action = data['action'] + self.vrf = data['vrf'] if 'vrf' in data else "default" + self.adj = data['adj'].split(',') if 'adj' in data else [] + + def get_locator(self): + return self.locator + + def get_opcode(self): + return self.opcode \ No newline at end of file From 3b8b9b72217ef7697f7b20f8abe4716124d3b6d0 Mon Sep 17 00:00:00 2001 From: BYGX-wcr Date: Wed, 4 Dec 2024 00:49:00 +0000 Subject: [PATCH 2/6] complete the set_handler and del_handler in the srv6 manager --- src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py | 77 ++++++++++++++++++---- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py b/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py index abca1d719c2d..d906fb2dbf8c 100644 --- a/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py +++ b/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py @@ -4,7 +4,18 @@ from .template import TemplateFabric import socket from swsscommon import swsscommon -from ipaddress import ip_network, IPv6Network +from ipaddress import IPv6Network, IPv6Address + +supported_SRv6_behaviors = { + 'end', + 'end.dt6', + 'end.dt46', + 'end.x', + 'uN', + 'uDT6', + 'uDT46', + 'uA' +} class SRv6Mgr(Manager): """ This class updates SRv6 configurations when SRV6_MY_SID_TABLE table is updated """ @@ -23,15 +34,41 @@ def __init__(self, common_objs, db, table): ) self.sids = {} # locators -> SIDs - self.config_db = None + self.config_db = swsscommon.SonicV2Connector() + self.config_db.connect(self.config_db.CONFIG_DB) def set_handler(self, key, data): ip_addr = key + if 'action' not in data: + log_err("Found a SRv6 config entry that does not specify action: {} | {}".format(ip_addr, data)) + return + + if data['action'] not in supported_SRv6_behaviors: + log_err("Found a SRv6 config entry associated with unsupported action: {} | {}".format(ip_addr, data)) + return + + if 'vrf' in data: + # verify that vrf name exists in the VRF_TABLE of CONFIG_DB + if not self.config_db.exists(self.config_db.CONFIG_DB, "VRF_TABLE|{}".format(data['vrf'])): + log_err("Found a SRv6 config entry that maps to an undefined VRF: {} | {}".format(ip_addr, data)) + return + sid = SID(ip_addr, data) locator = sid.get_locator() opcode = sid.get_opcode() - # TODO: generate FRR commands and push to Config Manager + cmd_list = ['segment-routing', 'srv6'] + cmd_list += ['locators', 'locator {}'.format(locator)] + if locator not in self.sids: + cmd_list += ['prefix {}/{} block-len {} node-len {}'.format(locator, sid.block_len + sid.node_len, sid.block_len, sid.node_len)] + + opcode_cmd = 'opcode ::{} {}'.format(opcode, sid.action) + if sid.vrf != 'default': + opcode_cmd += "vrf " + sid.vrf + cmd_list.append(opcode_cmd) + + self.cfg_mgr.push_list(cmd_list) + log_debug("{} SRv6 static configuration {} is scheduled for updates. {}".format(self.db_name, key, str(cmd_list))) self.sids.setdefault(locator, {})[opcode] = sid @@ -42,10 +79,30 @@ def del_handler(self, key, data): opcode = sid.get_opcode() if locator in self.sids: - #TODO: delete FRR opcode config - pass + if opcode not in self.sids[locator]: + log_warn("Encountered a config deletion with an unexpected SRv6 opcode: {} | {}".format(ip_addr, data)) + return + + cmd_list = ['segment-routing', 'srv6'] + cmd_list.append('locators') + if len(self.sids[locator] == 1): + # this is the last opcode of the locator, so we should delete the whole locator + cmd_list.append('no locator {}'.format(locator)) + + self.sids.pop(locator) + else: + # delete this opcode only + opcode_cmd = 'no opcode ::{} {}'.format(opcode, sid.action) + if sid.vrf != 'default': + opcode_cmd += "vrf " + sid.vrf + cmd_list.append(opcode_cmd) + + self.sids[locator].pop(opcode) + + self.cfg_mgr.push_list(cmd_list) + log_debug("{} SRv6 static configuration {} is scheduled for updates. {}".format(self.db_name, key, str(cmd_list))) else: - log_warn("Encountered an unexpected config change") + log_warn("Encountered a config deletion with an unexpected SRv6 locator: {} | {}".format(ip_addr, data)) return class SID: @@ -62,7 +119,7 @@ def __init__(self, ip_addr, data): locator_mask <<= 1 locator_mask |= 0x01 locator_mask <<= 128 - (self.block_len + self.node_len) - self.locator = self.bits & locator_mask + self.locator = IPv6Address(self.bits & locator_mask) # extract the opcode(function id) func_mask = 0 @@ -70,11 +127,7 @@ def __init__(self, ip_addr, data): func_mask <<= 1 func_mask != 0x01 func_mask <<= 128 - (self.block_len + self.node_len + self.func_len) - self.opcode = self.bits & func_mask - - if 'action' not in data: - log_err("Found a SRv6 config entry that does not specify action: {}|{}".format(ip_addr, data)) - raise RuntimeError("SID creation encountered error!") + self.opcode = IPv6Address(self.bits & func_mask) self.action = data['action'] self.vrf = data['vrf'] if 'vrf' in data else "default" From 309437dd203b0e1294cd0176bd4cacbb20d621b9 Mon Sep 17 00:00:00 2001 From: BYGX-wcr Date: Wed, 4 Dec 2024 17:52:37 +0000 Subject: [PATCH 3/6] add srv6 manager to the main.py and optimize the srv6 manager code slightly --- src/sonic-bgpcfgd/bgpcfgd/main.py | 3 +++ src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py | 7 ++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sonic-bgpcfgd/bgpcfgd/main.py b/src/sonic-bgpcfgd/bgpcfgd/main.py index 4b41e6825f95..860866465030 100644 --- a/src/sonic-bgpcfgd/bgpcfgd/main.py +++ b/src/sonic-bgpcfgd/bgpcfgd/main.py @@ -22,6 +22,7 @@ from .managers_rm import RouteMapMgr from .managers_device_global import DeviceGlobalCfgMgr from .managers_chassis_app_db import ChassisAppDbMgr +from .managers_srv6 import SRv6Mgr from .static_rt_timer import StaticRouteTimer from .runner import Runner, signal_handler from .template import TemplateFabric @@ -75,6 +76,8 @@ def do_work(): RouteMapMgr(common_objs, "APPL_DB", swsscommon.APP_BGP_PROFILE_TABLE_NAME), # Device Global Manager DeviceGlobalCfgMgr(common_objs, "CONFIG_DB", swsscommon.CFG_BGP_DEVICE_GLOBAL_TABLE_NAME), + # SRv6 Manager + SRv6Mgr(common_objs, "CONFIG_DB", "SRV6_MY_SID_TABLE") ] if device_info.is_chassis(): diff --git a/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py b/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py index d906fb2dbf8c..df95a5a96eb4 100644 --- a/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py +++ b/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py @@ -1,8 +1,5 @@ -import traceback -from .log import log_crit, log_err, log_debug, log_warn +from .log import log_err, log_debug, log_warn from .manager import Manager -from .template import TemplateFabric -import socket from swsscommon import swsscommon from ipaddress import IPv6Network, IPv6Address @@ -33,7 +30,7 @@ def __init__(self, common_objs, db, table): table, ) - self.sids = {} # locators -> SIDs + self.sids = {} # locators -> opcode -> SIDs self.config_db = swsscommon.SonicV2Connector() self.config_db.connect(self.config_db.CONFIG_DB) From 4cf7e751b5637c997d44549d02ffff7e2267a00a Mon Sep 17 00:00:00 2001 From: BYGX-wcr Date: Thu, 5 Dec 2024 02:48:44 +0000 Subject: [PATCH 4/6] fix missing ret val in set_handler and add UT cases --- src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py | 9 +- src/sonic-bgpcfgd/tests/test_srv6.py | 185 +++++++++++++++++++++ 2 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 src/sonic-bgpcfgd/tests/test_srv6.py diff --git a/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py b/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py index df95a5a96eb4..a4db57b882d3 100644 --- a/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py +++ b/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py @@ -38,17 +38,17 @@ def set_handler(self, key, data): ip_addr = key if 'action' not in data: log_err("Found a SRv6 config entry that does not specify action: {} | {}".format(ip_addr, data)) - return + return False if data['action'] not in supported_SRv6_behaviors: log_err("Found a SRv6 config entry associated with unsupported action: {} | {}".format(ip_addr, data)) - return + return False if 'vrf' in data: # verify that vrf name exists in the VRF_TABLE of CONFIG_DB if not self.config_db.exists(self.config_db.CONFIG_DB, "VRF_TABLE|{}".format(data['vrf'])): log_err("Found a SRv6 config entry that maps to an undefined VRF: {} | {}".format(ip_addr, data)) - return + return False sid = SID(ip_addr, data) locator = sid.get_locator() @@ -68,6 +68,7 @@ def set_handler(self, key, data): log_debug("{} SRv6 static configuration {} is scheduled for updates. {}".format(self.db_name, key, str(cmd_list))) self.sids.setdefault(locator, {})[opcode] = sid + return True def del_handler(self, key, data): ip_addr = key @@ -104,7 +105,7 @@ def del_handler(self, key, data): class SID: def __init__(self, ip_addr, data): - self.bits = int(IPv6Network(ip_addr).network_address) + self.bits = int(IPv6Address(ip_addr)) self.block_len = data['block_len'] if 'block_len' in data else 32 self.node_len = data['node_len'] if 'node_len' in data else 16 self.func_len = data['func_len'] if 'func_len' in data else 16 diff --git a/src/sonic-bgpcfgd/tests/test_srv6.py b/src/sonic-bgpcfgd/tests/test_srv6.py new file mode 100644 index 000000000000..54c0d7b825eb --- /dev/null +++ b/src/sonic-bgpcfgd/tests/test_srv6.py @@ -0,0 +1,185 @@ +from unittest.mock import MagicMock, patch + +from bgpcfgd.directory import Directory +from bgpcfgd.template import TemplateFabric +from bgpcfgd.managers_srv6 import SRv6Mgr +from swsscommon import swsscommon + +def constructor(): + cfg_mgr = MagicMock() + + common_objs = { + 'directory': Directory(), + 'cfg_mgr': cfg_mgr, + 'tf': TemplateFabric(), + 'constants': {}, + } + + mgr = SRv6Mgr(common_objs, "CONFIG_DB", "SRV6_MY_SID_TABLE") + assert len(mgr.sids) == 0 + + return mgr + +def op_test(mgr: SRv6Mgr, op, args, expected_ret, expected_cmds): + op_test.push_list_called = False + def push_list_checker(cmds): + op_test.push_list_called = True + assert len(cmds) == len(expected_cmds) + for i in range(len(expected_cmds)): + assert cmds[i] == expected_cmds[i] + return True + mgr.cfg_mgr.push_list = push_list_checker + + if op == 'SET': + ret = mgr.set_handler(*args) + mgr.cfg_mgr.push_list = MagicMock() + assert expected_ret == ret + elif op == 'DEL': + mgr.del_handler(*args) + mgr.cfg_mgr.push_list = MagicMock() + else: + mgr.cfg_mgr.push_list = MagicMock() + assert False, "Unexpected operation {}".format(op) + + if expected_cmds: + assert op_test.push_list_called, "cfg_mgr.push_list wasn't called" + else: + assert not op_test.push_list_called, "cfg_mgr.push_list was called" + +def test_uN_add(): + mgr = constructor() + + op_test(mgr, 'SET', ("FCBB:BBBB:20::", { + 'action': 'uN' + }), expected_ret=True, expected_cmds=[ + 'segment-routing', + 'srv6', + 'locators', + 'locator FCBB:BBBB:20:: block-len 32 node-len 16', + 'prefix FCBB:BBBB:20::/48' + ]) + +def test_uDT46_add_Vrf1(): + mgr = constructor() + + _old_exists = swsscommon.SonicV2Connector().exists + swsscommon.SonicV2Connector().exists = lambda x: True + op_test(mgr, 'SET', ("FCBB:BBBB:20:F1::", { + 'action': 'uDT46', + 'vrf': 'Vrf1' + }), expected_ret=True, expected_cmds=[ + 'segment-routing', + 'srv6', + 'locators', + 'locator FCBB:BBBB:20:: block-len 32 node-len 16', + 'prefix FCBB:BBBB:20::/48', + 'opcode ::F1 uDT46 vrf Vrf1' + ]) + swsscommon.SonicV2Connector().exists = _old_exists + +def test_uDT46_add_default_vrf(): + mgr = constructor() + + _old_exists = swsscommon.SonicV2Connector().exists + swsscommon.SonicV2Connector().exists = lambda x: True + op_test(mgr, 'SET', ("FCBB:BBBB:20:F2::", { + 'action': 'uDT46' + }), expected_ret=True, expected_cmds=[ + 'segment-routing', + 'srv6', + 'locators', + 'locator FCBB:BBBB:20:: block-len 32 node-len 16', + 'prefix FCBB:BBBB:20::/48', + 'opcode ::F2 uDT46' + ]) + swsscommon.SonicV2Connector().exists = _old_exists + +def test_uA_add(): + mgr = constructor() + + op_test(mgr, 'SET', ("FCBB:BBBB:20:F3::", { + 'action': 'uA', + 'adj': ["FCBB:BBBB:10::1", "FCBB:BBBB:10::2"] + }), expected_ret=True, expected_cmds=[ + 'segment-routing', + 'srv6', + 'locators', + 'locator FCBB:BBBB:20:: block-len 32 node-len 16', + 'prefix FCBB:BBBB:20::/48', + 'opcode ::F3 uA FCBB:BBBB:10::1 FCBB:BBBB:10::2' + ]) + +def test_uN_del(): + mgr = constructor() + + # add uN function first + mgr.set_handler("FCBB:BBBB:20::", { + 'action': 'uN' + }) + + # test the deletion + op_test(mgr, 'DEL', ("FCBB:BBBB:20::", { + 'action': 'uN' + }), expected_ret=True, expected_cmds=[ + 'segment-routing', + 'srv6', + 'locators', + 'no locator FCBB:BBBB:20:: block-len 32 node-len 16' + ]) + +def test_uDT46_del(): + mgr = constructor() + + # add a uN action first to make the uDT46 action not the last function + mgr.set_handler("FCBB:BBBB:20::", { + 'action': 'uN' + }) + + # add the uDT46 action + mgr.set_handler("FCBB:BBBB:20:F1::", { + 'action': 'uDT46', + 'vrf': 'Vrf1' + }) + + # test the deletion of uDT46 + _old_exists = swsscommon.SonicV2Connector().exists + swsscommon.SonicV2Connector().exists = lambda x: True + op_test(mgr, 'DEL', ("FCBB:BBBB:20:F1::", { + 'action': 'uDT46', + 'vrf': 'Vrf1' + }), expected_ret=True, expected_cmds=[ + 'segment-routing', + 'srv6', + 'locators', + 'locator FCBB:BBBB:20:: block-len 32 node-len 16', + 'prefix FCBB:BBBB:20::/48', + 'opcode ::F1 uDT46 vrf Vrf1' + ]) + swsscommon.SonicV2Connector().exists = _old_exists + +def test_uA_del(): + mgr = constructor() + + # add a uN action first to make the uA action not the last function + mgr.set_handler("FCBB:BBBB:20::", { + 'action': 'uN' + }) + + # add the uA action + mgr.set_handler("FCBB:BBBB:20:F3::", { + 'action': 'uA', + 'adj': ["FCBB:BBBB:10::1", "FCBB:BBBB:10::2"] + }) + + # test the deletion of uA + op_test(mgr, 'DEL', ("FCBB:BBBB:20:F3::", { + 'action': 'uA', + 'adj': ["FCBB:BBBB:10::1", "FCBB:BBBB:10::2"] + }), expected_ret=True, expected_cmds=[ + 'segment-routing', + 'srv6', + 'locators', + 'locator FCBB:BBBB:20:: block-len 32 node-len 16', + 'prefix FCBB:BBBB:20::/48', + 'no opcode ::F3 uA FCBB:BBBB:10::1 FCBB:BBBB:10::2' + ]) \ No newline at end of file From a942a9f7e95a1c90240f0ce62d4615e91d88672b Mon Sep 17 00:00:00 2001 From: BYGX-wcr Date: Fri, 13 Dec 2024 00:32:36 +0000 Subject: [PATCH 5/6] reorg the code based on updates from HLD and FRR CLI --- src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py | 22 +----- src/sonic-bgpcfgd/tests/test_srv6.py | 92 ++++------------------ 2 files changed, 19 insertions(+), 95 deletions(-) diff --git a/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py b/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py index a4db57b882d3..210607d47cb8 100644 --- a/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py +++ b/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py @@ -1,17 +1,12 @@ from .log import log_err, log_debug, log_warn from .manager import Manager from swsscommon import swsscommon -from ipaddress import IPv6Network, IPv6Address +from ipaddress import IPv6Address supported_SRv6_behaviors = { - 'end', - 'end.dt6', - 'end.dt46', - 'end.x', 'uN', 'uDT6', 'uDT46', - 'uA' } class SRv6Mgr(Manager): @@ -44,24 +39,16 @@ def set_handler(self, key, data): log_err("Found a SRv6 config entry associated with unsupported action: {} | {}".format(ip_addr, data)) return False - if 'vrf' in data: - # verify that vrf name exists in the VRF_TABLE of CONFIG_DB - if not self.config_db.exists(self.config_db.CONFIG_DB, "VRF_TABLE|{}".format(data['vrf'])): - log_err("Found a SRv6 config entry that maps to an undefined VRF: {} | {}".format(ip_addr, data)) - return False - - sid = SID(ip_addr, data) + sid = SID(ip_addr, data) # the information in data will be parsed into SID's attributes locator = sid.get_locator() opcode = sid.get_opcode() cmd_list = ['segment-routing', 'srv6'] cmd_list += ['locators', 'locator {}'.format(locator)] if locator not in self.sids: - cmd_list += ['prefix {}/{} block-len {} node-len {}'.format(locator, sid.block_len + sid.node_len, sid.block_len, sid.node_len)] + cmd_list += ['prefix {}/{} block-len {} node-len {} func-bits {}'.format(locator, sid.block_len + sid.node_len, sid.block_len, sid.node_len, sid.func_len)] opcode_cmd = 'opcode ::{} {}'.format(opcode, sid.action) - if sid.vrf != 'default': - opcode_cmd += "vrf " + sid.vrf cmd_list.append(opcode_cmd) self.cfg_mgr.push_list(cmd_list) @@ -90,9 +77,8 @@ def del_handler(self, key, data): self.sids.pop(locator) else: # delete this opcode only + cmd_list.append('locator {}'.format(locator)) opcode_cmd = 'no opcode ::{} {}'.format(opcode, sid.action) - if sid.vrf != 'default': - opcode_cmd += "vrf " + sid.vrf cmd_list.append(opcode_cmd) self.sids[locator].pop(opcode) diff --git a/src/sonic-bgpcfgd/tests/test_srv6.py b/src/sonic-bgpcfgd/tests/test_srv6.py index 54c0d7b825eb..e73f1b4bc54a 100644 --- a/src/sonic-bgpcfgd/tests/test_srv6.py +++ b/src/sonic-bgpcfgd/tests/test_srv6.py @@ -49,33 +49,16 @@ def push_list_checker(cmds): def test_uN_add(): mgr = constructor() - op_test(mgr, 'SET', ("FCBB:BBBB:20::", { - 'action': 'uN' - }), expected_ret=True, expected_cmds=[ - 'segment-routing', - 'srv6', - 'locators', - 'locator FCBB:BBBB:20:: block-len 32 node-len 16', - 'prefix FCBB:BBBB:20::/48' - ]) - -def test_uDT46_add_Vrf1(): - mgr = constructor() - - _old_exists = swsscommon.SonicV2Connector().exists - swsscommon.SonicV2Connector().exists = lambda x: True op_test(mgr, 'SET', ("FCBB:BBBB:20:F1::", { - 'action': 'uDT46', - 'vrf': 'Vrf1' + 'action': 'uN' }), expected_ret=True, expected_cmds=[ 'segment-routing', 'srv6', 'locators', - 'locator FCBB:BBBB:20:: block-len 32 node-len 16', + 'locator FCBB:BBBB:20:: block-len 32 node-len 16 func-bits 16', 'prefix FCBB:BBBB:20::/48', - 'opcode ::F1 uDT46 vrf Vrf1' + 'opcode ::F1 uN' ]) - swsscommon.SonicV2Connector().exists = _old_exists def test_uDT46_add_default_vrf(): mgr = constructor() @@ -88,98 +71,53 @@ def test_uDT46_add_default_vrf(): 'segment-routing', 'srv6', 'locators', - 'locator FCBB:BBBB:20:: block-len 32 node-len 16', + 'locator FCBB:BBBB:20:: block-len 32 node-len 16 func-bits 16', 'prefix FCBB:BBBB:20::/48', 'opcode ::F2 uDT46' ]) swsscommon.SonicV2Connector().exists = _old_exists -def test_uA_add(): - mgr = constructor() - - op_test(mgr, 'SET', ("FCBB:BBBB:20:F3::", { - 'action': 'uA', - 'adj': ["FCBB:BBBB:10::1", "FCBB:BBBB:10::2"] - }), expected_ret=True, expected_cmds=[ - 'segment-routing', - 'srv6', - 'locators', - 'locator FCBB:BBBB:20:: block-len 32 node-len 16', - 'prefix FCBB:BBBB:20::/48', - 'opcode ::F3 uA FCBB:BBBB:10::1 FCBB:BBBB:10::2' - ]) - def test_uN_del(): mgr = constructor() # add uN function first - mgr.set_handler("FCBB:BBBB:20::", { + mgr.set_handler("FCBB:BBBB:20:F1::", { 'action': 'uN' }) # test the deletion - op_test(mgr, 'DEL', ("FCBB:BBBB:20::", { + op_test(mgr, 'DEL', ("FCBB:BBBB:20:F1::", { 'action': 'uN' }), expected_ret=True, expected_cmds=[ 'segment-routing', 'srv6', 'locators', - 'no locator FCBB:BBBB:20:: block-len 32 node-len 16' + 'no locator FCBB:BBBB:20:: block-len 32 node-len 16 func-bits 16' ]) def test_uDT46_del(): mgr = constructor() # add a uN action first to make the uDT46 action not the last function - mgr.set_handler("FCBB:BBBB:20::", { + mgr.set_handler("FCBB:BBBB:20:F1::", { 'action': 'uN' }) # add the uDT46 action - mgr.set_handler("FCBB:BBBB:20:F1::", { - 'action': 'uDT46', - 'vrf': 'Vrf1' + mgr.set_handler("FCBB:BBBB:20:F2::", { + 'action': 'uDT46' }) # test the deletion of uDT46 _old_exists = swsscommon.SonicV2Connector().exists swsscommon.SonicV2Connector().exists = lambda x: True - op_test(mgr, 'DEL', ("FCBB:BBBB:20:F1::", { - 'action': 'uDT46', - 'vrf': 'Vrf1' + op_test(mgr, 'DEL', ("FCBB:BBBB:20:F2::", { + 'action': 'uDT46' }), expected_ret=True, expected_cmds=[ 'segment-routing', 'srv6', 'locators', - 'locator FCBB:BBBB:20:: block-len 32 node-len 16', - 'prefix FCBB:BBBB:20::/48', - 'opcode ::F1 uDT46 vrf Vrf1' + 'locator FCBB:BBBB:20:: block-len 32 node-len 16 func-bits 16', + 'no opcode ::F2 uDT46' ]) - swsscommon.SonicV2Connector().exists = _old_exists - -def test_uA_del(): - mgr = constructor() - - # add a uN action first to make the uA action not the last function - mgr.set_handler("FCBB:BBBB:20::", { - 'action': 'uN' - }) - - # add the uA action - mgr.set_handler("FCBB:BBBB:20:F3::", { - 'action': 'uA', - 'adj': ["FCBB:BBBB:10::1", "FCBB:BBBB:10::2"] - }) - - # test the deletion of uA - op_test(mgr, 'DEL', ("FCBB:BBBB:20:F3::", { - 'action': 'uA', - 'adj': ["FCBB:BBBB:10::1", "FCBB:BBBB:10::2"] - }), expected_ret=True, expected_cmds=[ - 'segment-routing', - 'srv6', - 'locators', - 'locator FCBB:BBBB:20:: block-len 32 node-len 16', - 'prefix FCBB:BBBB:20::/48', - 'no opcode ::F3 uA FCBB:BBBB:10::1 FCBB:BBBB:10::2' - ]) \ No newline at end of file + swsscommon.SonicV2Connector().exists = _old_exists \ No newline at end of file From 806ea90840f436b3d39f4b271c83e526307b0cf7 Mon Sep 17 00:00:00 2001 From: BYGX-wcr Date: Tue, 17 Dec 2024 01:35:07 +0000 Subject: [PATCH 6/6] add vrf back in usage and update CLI format --- src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py | 43 ++++++++++++---------- src/sonic-bgpcfgd/tests/test_srv6.py | 17 +++++---- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py b/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py index 210607d47cb8..01de34a21fc4 100644 --- a/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py +++ b/src/sonic-bgpcfgd/bgpcfgd/managers_srv6.py @@ -5,10 +5,11 @@ supported_SRv6_behaviors = { 'uN', - 'uDT6', 'uDT46', } +DEFAULT_VRF = "default" + class SRv6Mgr(Manager): """ This class updates SRv6 configurations when SRV6_MY_SID_TABLE table is updated """ def __init__(self, common_objs, db, table): @@ -25,7 +26,7 @@ def __init__(self, common_objs, db, table): table, ) - self.sids = {} # locators -> opcode -> SIDs + self.sids = {} # locators -> func_bits -> SIDs self.config_db = swsscommon.SonicV2Connector() self.config_db.connect(self.config_db.CONFIG_DB) @@ -41,47 +42,51 @@ def set_handler(self, key, data): sid = SID(ip_addr, data) # the information in data will be parsed into SID's attributes locator = sid.get_locator() - opcode = sid.get_opcode() + func_bits = sid.get_func_bits() cmd_list = ['segment-routing', 'srv6'] cmd_list += ['locators', 'locator {}'.format(locator)] if locator not in self.sids: cmd_list += ['prefix {}/{} block-len {} node-len {} func-bits {}'.format(locator, sid.block_len + sid.node_len, sid.block_len, sid.node_len, sid.func_len)] - opcode_cmd = 'opcode ::{} {}'.format(opcode, sid.action) - cmd_list.append(opcode_cmd) + sid_cmd = 'sid {}/{} {}'.format(ip_addr, sid.block_len + sid.node_len + sid.func_len, sid.action) + if sid.vrf != DEFAULT_VRF: + sid_cmd += ' vrf {}'.format(sid.vrf) + cmd_list.append(sid_cmd) self.cfg_mgr.push_list(cmd_list) log_debug("{} SRv6 static configuration {} is scheduled for updates. {}".format(self.db_name, key, str(cmd_list))) - self.sids.setdefault(locator, {})[opcode] = sid + self.sids.setdefault(locator, {})[func_bits] = sid return True def del_handler(self, key, data): ip_addr = key sid = SID(ip_addr, data) locator = sid.get_locator() - opcode = sid.get_opcode() + func_bits = sid.get_func_bits() if locator in self.sids: - if opcode not in self.sids[locator]: - log_warn("Encountered a config deletion with an unexpected SRv6 opcode: {} | {}".format(ip_addr, data)) + if func_bits not in self.sids[locator]: + log_warn("Encountered a config deletion with an unexpected SRv6 action: {} | {}".format(ip_addr, data)) return cmd_list = ['segment-routing', 'srv6'] cmd_list.append('locators') if len(self.sids[locator] == 1): - # this is the last opcode of the locator, so we should delete the whole locator + # this is the last func_bits of the locator, so we should delete the whole locator cmd_list.append('no locator {}'.format(locator)) self.sids.pop(locator) else: - # delete this opcode only + # delete this func_bits only cmd_list.append('locator {}'.format(locator)) - opcode_cmd = 'no opcode ::{} {}'.format(opcode, sid.action) - cmd_list.append(opcode_cmd) + no_sid_cmd = 'no sid {}/{} {}'.format(ip_addr, sid.block_len + sid.node_len + sid.func_len, sid.action) + if sid.vrf != DEFAULT_VRF: + no_sid_cmd += ' vrf {}'.format(sid.vrf) + cmd_list.append(no_sid_cmd) - self.sids[locator].pop(opcode) + self.sids[locator].pop(func_bits) self.cfg_mgr.push_list(cmd_list) log_debug("{} SRv6 static configuration {} is scheduled for updates. {}".format(self.db_name, key, str(cmd_list))) @@ -105,20 +110,20 @@ def __init__(self, ip_addr, data): locator_mask <<= 128 - (self.block_len + self.node_len) self.locator = IPv6Address(self.bits & locator_mask) - # extract the opcode(function id) + # extract the func_bits (function id) func_mask = 0 for i in range(self.func_len): func_mask <<= 1 func_mask != 0x01 func_mask <<= 128 - (self.block_len + self.node_len + self.func_len) - self.opcode = IPv6Address(self.bits & func_mask) + self.func_bits = IPv6Address(self.bits & func_mask) self.action = data['action'] - self.vrf = data['vrf'] if 'vrf' in data else "default" + self.vrf = data['vrf'] if 'vrf' in data else DEFAULT_VRF self.adj = data['adj'].split(',') if 'adj' in data else [] def get_locator(self): return self.locator - def get_opcode(self): - return self.opcode \ No newline at end of file + def get_func_bits(self): + return self.func_bits \ No newline at end of file diff --git a/src/sonic-bgpcfgd/tests/test_srv6.py b/src/sonic-bgpcfgd/tests/test_srv6.py index e73f1b4bc54a..4645fd6eebff 100644 --- a/src/sonic-bgpcfgd/tests/test_srv6.py +++ b/src/sonic-bgpcfgd/tests/test_srv6.py @@ -57,23 +57,25 @@ def test_uN_add(): 'locators', 'locator FCBB:BBBB:20:: block-len 32 node-len 16 func-bits 16', 'prefix FCBB:BBBB:20::/48', - 'opcode ::F1 uN' + 'sid FCBB:BBBB:20:F1::/64 uN' ]) -def test_uDT46_add_default_vrf(): +def test_uDT46_add_vrf1(): mgr = constructor() _old_exists = swsscommon.SonicV2Connector().exists swsscommon.SonicV2Connector().exists = lambda x: True + op_test(mgr, 'SET', ("FCBB:BBBB:20:F2::", { - 'action': 'uDT46' + 'action': 'uDT46', + 'vrf': 'vrf1' }), expected_ret=True, expected_cmds=[ 'segment-routing', 'srv6', 'locators', 'locator FCBB:BBBB:20:: block-len 32 node-len 16 func-bits 16', 'prefix FCBB:BBBB:20::/48', - 'opcode ::F2 uDT46' + 'sid FCBB:BBBB:20:F2::/64 uDT46 vrf vrf1' ]) swsscommon.SonicV2Connector().exists = _old_exists @@ -95,7 +97,7 @@ def test_uN_del(): 'no locator FCBB:BBBB:20:: block-len 32 node-len 16 func-bits 16' ]) -def test_uDT46_del(): +def test_uDT46_del_vrf1(): mgr = constructor() # add a uN action first to make the uDT46 action not the last function @@ -112,12 +114,13 @@ def test_uDT46_del(): _old_exists = swsscommon.SonicV2Connector().exists swsscommon.SonicV2Connector().exists = lambda x: True op_test(mgr, 'DEL', ("FCBB:BBBB:20:F2::", { - 'action': 'uDT46' + 'action': 'uDT46', + "vrf": "vrf1" }), expected_ret=True, expected_cmds=[ 'segment-routing', 'srv6', 'locators', 'locator FCBB:BBBB:20:: block-len 32 node-len 16 func-bits 16', - 'no opcode ::F2 uDT46' + 'no sid FCBB:BBBB:20:F2/64 uDT46 vrf vrf1' ]) swsscommon.SonicV2Connector().exists = _old_exists \ No newline at end of file