diff --git a/tests/bgp/test_bgp_speaker.py b/tests/bgp/test_bgp_speaker.py index 888926e471a..0294c2ca063 100644 --- a/tests/bgp/test_bgp_speaker.py +++ b/tests/bgp/test_bgp_speaker.py @@ -15,6 +15,7 @@ from tests.common.utilities import wait_tcp_connection from tests.common.helpers.assertions import pytest_require from tests.common.utilities import wait_until +from tests.flow_counter.flow_counter_utils import RouteFlowCounterTestContext, is_route_flow_counter_supported # lgtm[py/unused-import] pytestmark = [ @@ -251,7 +252,7 @@ def is_all_neighbors_learned(duthost, speaker_ips): def bgp_speaker_announce_routes_common(common_setup_teardown, tbinfo, duthost, ptfhost, ipv4, ipv6, mtu, - family, prefix, nexthop_ips, vlan_mac): + family, prefix, nexthop_ips, vlan_mac, is_route_flow_counter_supported): """Setup bgp speaker on T0 topology and verify routes advertised by bgp speaker is received by T0 TOR """ @@ -308,19 +309,21 @@ def bgp_speaker_announce_routes_common(common_setup_teardown, ptfhost.copy(content=json.dumps(ptf_test_port_map), dest=PTF_TEST_PORT_MAP) logger.info("run ptf test") - - ptf_runner(ptfhost, - "ptftests", - "fib_test.FibTest", - platform_dir="ptftests", - params={"ptf_test_port_map": PTF_TEST_PORT_MAP, - "fib_info_files": ["/root/bgp_speaker_route_%s.txt" % family], - "ipv4": ipv4, - "ipv6": ipv6, - "testbed_mtu": mtu, - "test_balancing": False}, - log_file="/tmp/bgp_speaker_test.FibTest.log", - socket_recv_size=16384) + expecte_packet_num = 3 + packet_size = 1518 + with RouteFlowCounterTestContext(is_route_flow_counter_supported, duthost, [prefix], {prefix : {'packets': expecte_packet_num, 'bytes': packet_size * expecte_packet_num}}): + ptf_runner(ptfhost, + "ptftests", + "fib_test.FibTest", + platform_dir="ptftests", + params={"ptf_test_port_map": PTF_TEST_PORT_MAP, + "fib_info_files": ["/root/bgp_speaker_route_%s.txt" % family], + "ipv4": ipv4, + "ipv6": ipv6, + "testbed_mtu": mtu, + "test_balancing": False}, + log_file="/tmp/bgp_speaker_test.FibTest.log", + socket_recv_size=16384) logger.info("Withdraw routes") withdraw_route(ptfip, lo_addr, prefix, nexthop_ips[1].ip, port_num[0]) @@ -331,20 +334,20 @@ def bgp_speaker_announce_routes_common(common_setup_teardown, @pytest.mark.parametrize("ipv4, ipv6, mtu", [pytest.param(True, False, 9114)]) -def test_bgp_speaker_announce_routes(common_setup_teardown, tbinfo, duthosts, rand_one_dut_hostname, ptfhost, ipv4, ipv6, mtu, vlan_mac): +def test_bgp_speaker_announce_routes(common_setup_teardown, tbinfo, duthosts, rand_one_dut_hostname, ptfhost, ipv4, ipv6, mtu, vlan_mac, is_route_flow_counter_supported): """Setup bgp speaker on T0 topology and verify routes advertised by bgp speaker is received by T0 TOR """ duthost = duthosts[rand_one_dut_hostname] nexthops = common_setup_teardown[3] - bgp_speaker_announce_routes_common(common_setup_teardown, tbinfo, duthost, ptfhost, ipv4, ipv6, mtu, "v4", "10.10.10.0/26", nexthops, vlan_mac) + bgp_speaker_announce_routes_common(common_setup_teardown, tbinfo, duthost, ptfhost, ipv4, ipv6, mtu, "v4", "10.10.10.0/26", nexthops, vlan_mac, is_route_flow_counter_supported) @pytest.mark.parametrize("ipv4, ipv6, mtu", [pytest.param(False, True, 9114)]) -def test_bgp_speaker_announce_routes_v6(common_setup_teardown, tbinfo, duthosts, rand_one_dut_hostname, ptfhost, ipv4, ipv6, mtu, vlan_mac): +def test_bgp_speaker_announce_routes_v6(common_setup_teardown, tbinfo, duthosts, rand_one_dut_hostname, ptfhost, ipv4, ipv6, mtu, vlan_mac, is_route_flow_counter_supported): """Setup bgp speaker on T0 topology and verify routes advertised by bgp speaker is received by T0 TOR """ duthost = duthosts[rand_one_dut_hostname] nexthops = common_setup_teardown[4] - bgp_speaker_announce_routes_common(common_setup_teardown, tbinfo, duthost, ptfhost, ipv4, ipv6, mtu, "v6", "fc00:10::/64", nexthops, vlan_mac) + bgp_speaker_announce_routes_common(common_setup_teardown, tbinfo, duthost, ptfhost, ipv4, ipv6, mtu, "v6", "fc00:10::/64", nexthops, vlan_mac, is_route_flow_counter_supported) diff --git a/tests/flow_counter/__init__.py b/tests/flow_counter/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/flow_counter/flow_counter_utils.py b/tests/flow_counter/flow_counter_utils.py new file mode 100644 index 00000000000..8d7817d7976 --- /dev/null +++ b/tests/flow_counter/flow_counter_utils.py @@ -0,0 +1,302 @@ +import allure +import logging +import pytest +import random +from tests.common.helpers.assertions import pytest_assert +from tests.common.utilities import wait_until, check_skip_release + +logger = logging.getLogger(__name__) + +skip_versions = ['201811', '201911', '202012', '202106', '202111'] +CAPABILITY_WAIT_TIME_IN_SEC = 180 +CAPABILITY_CHECK_INTERVAL_IN_SEC = 5 + + +class RouteFlowCounterTestContext: + """Allow caller to use "with" key words to run router flow counter test. + """ + def __init__(self, support, dut, route_pattern_list, expected_stats, interval=1000): + """Init RouteFlowCounterTestContext + + Args: + dut (object): DUT object + route_pattern_list (list): a list of route pattern, e.g. ['1.1.1.0/24', 'Vrf1|1.1.1.0/24', 'Vnet1|2.2.2.0/24'] + expected_stats (dict): Expected result value. e.g. {'1.1.1.0/24': {'packets': '5', 'bytes': '4500'}} + interval (int, optional): Route flow counter query interval. Defaults to 1000. + """ + self.dut = dut + self.route_pattern_list = route_pattern_list + self.expected_stats = expected_stats + self.interval = interval + self.is_route_flow_counter_supported = support + + def __enter__(self): + """Enable route flow counter and configure route pattern + """ + if not self.is_route_flow_counter_supported: + return + with allure.step('Enable route flow counter and config route flow pattern: {}'.format(','.join(self.route_pattern_list))): + set_route_flow_counter_interval(self.dut, self.interval) + set_route_flow_counter_status(self.dut, True) + for route_pattern in self.route_pattern_list: + set_route_flow_counter_pattern(self.dut, route_pattern) + + def __exit__(self, exc_type, exc_val, exc_tb): + """Do following tasks: + 1. Verify route flow counter stats agaist expected value + 2. Disable route flow coutern and remove route pattern + + Args: + exc_type (object): not used + exc_val (object): not used + exc_tb (object): not used + """ + if not self.is_route_flow_counter_supported: + return + + try: + result, message = self.check_stats() + pytest_assert(result, message) + finally: + set_route_flow_counter_status(self.dut, False) + for route_pattern in self.route_pattern_list: + remove_route_flow_counter_pattern(self.dut, route_pattern) + + + def check_stats(self): + """Verify route flow counter statistic + + Returns: + tuple: (status, error message) + """ + logger.info('Checking route flow counter stats') + with allure.step('Checking route flow counter stats'): + actual_stats = parse_route_flow_counter_stats(self.dut) + result, message = verify_route_flow_counter_stats(self.expected_stats, actual_stats) + if not result: + return result, message + + if len(self.expected_stats) > 0: + logger.info('Checking route flow counter stats after clearing by route') + with allure.step('Checking route flow counter stats after clearing by route'): + to_clear = random.sample(list(self.expected_stats.keys()), 1)[0] + clear_route_flow_counter_by_route(self.dut, to_clear) + for key in self.expected_stats[to_clear]: + self.expected_stats[to_clear][key] = '0' + actual_stats = parse_route_flow_counter_stats(self.dut) + result, message = verify_route_flow_counter_stats(self.expected_stats, actual_stats) + if not result: + return result, message + + with allure.step('Checking route flow counter stats after clearing by pattern or clearing all'): + if len(self.expected_stats) == 1 and len(self.route_pattern_list) == 1: + logger.info('Checking route flow counter stats after clearing by pattern') + clear_route_flow_counter_by_pattern(self.dut, self.route_pattern_list[0]) + else: + logger.info('Checking route flow counter stats after clearing all routes') + clear_route_flow_counter(self.dut) + for prefix, value in self.expected_stats.items(): + for key in value: + self.expected_stats[prefix][key] = '0' + + actual_stats = parse_route_flow_counter_stats(self.dut) + return verify_route_flow_counter_stats(self.expected_stats, actual_stats) + + +@pytest.fixture(scope = "module") +def is_route_flow_counter_supported(duthosts, enum_rand_one_per_hwsku_hostname): + """Check if route flow counter is supported on this platform + + Args: + dut (object): DUT object + + Returns: + bool: True if supported + """ + rand_selected_dut = duthosts[enum_rand_one_per_hwsku_hostname] + skip, _ = check_skip_release(rand_selected_dut, skip_versions) + if skip: + logger.info('Route flow counter is not supported on these versions: {}'.format(skip_versions)) + return False + + route_flow_counter_capability = [] # Use a list to store the capability + if not wait_until(CAPABILITY_WAIT_TIME_IN_SEC, CAPABILITY_CHECK_INTERVAL_IN_SEC, 0, get_route_flow_counter_capability, rand_selected_dut, route_flow_counter_capability): + pytest_assert(False, 'Failed to get route flow counter capability') + if not route_flow_counter_capability[0]: + logger.info('Route flow counter is not supported on this platform') + return route_flow_counter_capability[0] + + +def get_route_flow_counter_capability(dut, route_flow_counter_capability): + """Get route flow counter capability from STATE DB + + Args: + dut (object): DUT object + + Returns: + bool: True if capability is successfully retrieved from STATE DB + """ + support = dut.shell('sudo sonic-db-cli STATE_DB HGET "FLOW_COUNTER_CAPABILITY_TABLE|route" support')['stdout'].strip() + if support == 'true': + route_flow_counter_capability.append(True) + elif support == 'false': + route_flow_counter_capability.append(False) + elif support: + # Impossible branch, just incase + pytest_assert(False, 'support field of FLOW_COUNTER_CAPABILITY_TABLE|route has invalid value {}'.format(support)) + return len(route_flow_counter_capability) > 0 + + +def set_route_flow_counter_status(dut, status): + """Set route flow counter status + + Args: + dut (object): DUT object + status (bool): Enable if True else disable + """ + dut.command('counterpoll flowcnt-route {}'.format('enable' if status else 'disable')) + + +def set_route_flow_counter_interval(dut, interval): + """Set route flow counter interval + + Args: + dut (object): DUT object + interval (int): Query interval value in ms + """ + dut.command('counterpoll flowcnt-route interval {}'.format(interval)) + + +def set_route_flow_counter_pattern(dut, route_pattern, max_match_count=30): + """Set route pattern for route flow counter + + Args: + dut (object): DUT object + route_pattern (str): Route pattern. e.g. "1.1.1.0/24", "2000::/64", "Vrf1|2.2.2.0/24" + max_match_count (int, optional): Max allowed match count. Defaults to 30. + """ + items = route_pattern.split('|') + if len(items) == 2: + dut.command('sudo config flowcnt-route pattern add {} --vrf {} --max {} -y'.format(items[1], items[0], max_match_count)) + elif len(items) == 1: + dut.command('sudo config flowcnt-route pattern add {} --max {} -y'.format(items[0], max_match_count)) + else: + logger.error('Invalid route pattern {}'.format(route_pattern)) + + +def remove_route_flow_counter_pattern(dut, route_pattern): + """Remove route pattern for route flow counter + + Args: + dut (object): DUT object + route_pattern (str): Route pattern. e.g. "1.1.1.0/24", "2000::/64", "Vrf1|2.2.2.0/24" + """ + items = route_pattern.split('|') + if len(items) == 2: + dut.command('sudo config flowcnt-route pattern remove {} --vrf {}'.format(items[1], items[0])) + elif len(items) == 1: + dut.command('sudo config flowcnt-route pattern remove {}'.format(items[0])) + else: + logger.error('Invalid route pattern {}'.format(route_pattern)) + +def remove_all_route_flow_counter_patterns(dut): + """Remove all route patterns + + Args: + dut (object): DUT object + """ + data = dut.show_and_parse('show flowcnt-route config') + for item in data: + prefix = item['route pattern'] + vrf = item['vrf'] + if vrf != 'default': + dut.command('sudo config flowcnt-route pattern remove {} --vrf {}'.format(prefix, vrf)) + else: + dut.command('sudo config flowcnt-route pattern remove {}'.format(prefix)) + + +def clear_route_flow_counter(dut): + """Clear all route flow counter statistics + + Args: + dut (object): DUT object + """ + dut.command('sonic-clear flowcnt-route') + + +def clear_route_flow_counter_by_pattern(dut, route_pattern): + """Clear route flow counter statistics by pattern + + Args: + dut (object): DUT object + route_pattern (str): Route pattern. e.g. "1.1.1.0/24", "2000::/64", "Vrf1|2.2.2.0/24" + """ + items = route_pattern.split('|') + if len(items) == 2: + dut.command('sonic-clear flowcnt-route pattern {} --vrf {}'.format(items[1], items[0])) + elif len(items) == 1: + dut.command('sonic-clear flowcnt-route pattern {}'.format(items[0])) + else: + logger.error('Invalid route pattern {}'.format(route_pattern)) + + +def clear_route_flow_counter_by_route(dut, prefix): + """Clear route flow counter statistics by route + + Args: + dut (object): DUT object + prefix (str): Prefix pattern. e.g. "1.1.1.0/24", "2000::/64", "Vrf1|2.2.2.0/24" + """ + items = prefix.split('|') + if len(items) == 2: + dut.command('sonic-clear flowcnt-route route {} --vrf {}'.format(items[1], items[0])) + elif len(items) == 1: + dut.command('sonic-clear flowcnt-route route {}'.format(items[0])) + else: + logger.error('Invalid prefix pattern {}'.format(prefix)) + + +def parse_route_flow_counter_stats(dut): + """Parse command output of "show flowcnt-route stats" + + Args: + dut (object): DUT object + + Returns: + dict: Parsed result. e.g. {'1.1.1.0/24': {'packets': '5', 'bytes': '4500'}} + """ + stats_list = dut.show_and_parse('show flowcnt-route stats') + parse_result = {} + for stats in stats_list: + if stats['vrf'] == 'default': + key = stats['matched routes'] + else: + key = '|'.join([stats['vrf'], stats['matched routes']]) + parse_result[key] = { + 'packets': stats['packets'], + 'bytes': stats['bytes'] + } + return parse_result + + +def verify_route_flow_counter_stats(expect_stats, actual_stats): + """Verify actual statistic with expected statistic + + Args: + expect_stats (dict): Expected stats. e.g. {'1.1.1.0/24': {'packets': '5', 'bytes': '4500'}} + actual_stats (dict): Actual stats. e.g. {'1.1.1.0/24': {'packets': '5', 'bytes': '4500'}} + + Returns: + bool: Match if True. + """ + logger.info('Expected stats: {}'.format(expect_stats)) + logger.info('Actual stats: {}'.format(actual_stats)) + for key, value in expect_stats.items(): + if key not in actual_stats: + return False, 'Failed to find {} in result'.format(key) + + for stats_type, expect_value in value.items(): + if int(expect_value) != int(actual_stats[key][stats_type].replace(',', '')): + return False, 'Expected {} value of {} is {}, but got {}'.format(stats_type, key, expect_value, actual_stats[key][stats_type]) + + return True, None diff --git a/tests/route/__init__.py b/tests/route/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/route/test_route_flow_counter.py b/tests/route/test_route_flow_counter.py new file mode 100644 index 00000000000..fb0f0051c0c --- /dev/null +++ b/tests/route/test_route_flow_counter.py @@ -0,0 +1,185 @@ +import allure +import logging +import pytest +from tests.common.helpers.assertions import pytest_assert, pytest_require +from tests.flow_counter import flow_counter_utils +from tests.flow_counter.flow_counter_utils import is_route_flow_counter_supported # lgtm[py/unused-import] + +logger = logging.getLogger(__name__) + +test_update_route_pattern_para = [ + { + 'is_ipv6': False, + 'route_pattern_a': '1.1.0.0/16', + 'route_pattern_b': '1.2.0.0/16', + 'prefix_a': '1.1.1.0/24', + 'prefix_b': '1.2.1.0/24', + }, + { + 'is_ipv6': True, + 'route_pattern_a': '1234:0:0:1::/64', + 'route_pattern_b': '1234:0:0:2::/64', + 'prefix_a': '1234:0:0:1::/64', + 'prefix_b': '1234:0:0:2::/64', + } +] + +added_routes = set() + + +@pytest.fixture(scope='function', autouse=True) +def skip_if_not_supported(is_route_flow_counter_supported): + """Skip the test if route flow counter is not supported on this platform + + Args: + rand_selected_dut (object): DUT object + """ + pytest_require(is_route_flow_counter_supported, 'route flow counter is not supported') + + +@pytest.fixture(scope='function', autouse=True) +def clear_route_flow_counter(rand_selected_dut): + """Clear route flow counter configuration + + Args: + rand_selected_dut (object): DUT object + """ + yield + + flow_counter_utils.set_route_flow_counter_status(rand_selected_dut, False) + flow_counter_utils.remove_all_route_flow_counter_patterns(rand_selected_dut) + for route in added_routes: + rand_selected_dut.shell('config route del prefix {} nexthop {}'.format(route[0], route[1]), module_ignore_errors=True) + added_routes.clear() + + +def add_route(duthost, prefix, nexthop): + """Add static route + + Args: + duthost (object): DUT object + prefix (str): Route prefix + nexthop (str): Route nexthop + """ + duthost.shell('config route add prefix {} nexthop {}'.format(prefix, nexthop)) + added_routes.add((prefix, nexthop)) + + +def del_route(duthost, prefix, nexthop): + """Remove static route + + Args: + duthost (object): DUT object + prefix (str): Route prefix + nexthop (str): Route nexthop + """ + duthost.shell('config route del prefix {} nexthop {}'.format(prefix, nexthop), module_ignore_errors=True) + added_routes.remove((prefix, nexthop)) + + +class TestRouteCounter: + @pytest.mark.parametrize("route_flow_counter_params", test_update_route_pattern_para) + def test_update_route_pattern(self, rand_selected_dut, route_flow_counter_params): + """Test steps: + 1. Add two routes a and b, configure route pattern match a + 2. Verify only route flow counter for a is created + 3. Update route pattern to match b + 4. Verify only route flow counter for b is created + + Args: + rand_selected_dut (object): DUT object + route_flow_counter_params (list): A list contains the test parameter. [ipv6, pattern_a, pattern_b, prefix_a, prefix_b] + """ + duthost = rand_selected_dut + ipv6 = route_flow_counter_params['is_ipv6'] + route_pattern_a = route_flow_counter_params['route_pattern_a'] + route_pattern_b = route_flow_counter_params['route_pattern_b'] + prefix_a = route_flow_counter_params['prefix_a'] + prefix_b = route_flow_counter_params['prefix_b'] + with allure.step('Enable route flow counter and config route pattern to {}'.format(route_pattern_a)): + flow_counter_utils.set_route_flow_counter_status(duthost, True) + flow_counter_utils.set_route_flow_counter_pattern(duthost, route_pattern_a) + + logger.info('Adding static route {} and {}'.format(prefix_a, prefix_b)) + with allure.step('Adding static route {} and {}'.format(prefix_a, prefix_b)): + nexthop_addr = self._get_nexthop(duthost, ipv6=ipv6) + add_route(rand_selected_dut, prefix_a, nexthop_addr) + add_route(rand_selected_dut, prefix_b, nexthop_addr) + + with allure.step('Route pattern is {}, verify route flow counter is bound to {}'.format(route_pattern_a, prefix_a)): + stats = flow_counter_utils.parse_route_flow_counter_stats(duthost) + pytest_assert(prefix_a in stats, 'Route flow counter for {} is not created'.format(prefix_a)) + pytest_assert(prefix_b not in stats, 'Route flow counter for {} should not be created'.format(prefix_b)) + + with allure.step('Change route flow pattern to {}, verify route flow counter is bound to {}'.format(route_pattern_b, prefix_b)): + flow_counter_utils.set_route_flow_counter_pattern(duthost, route_pattern_b) + stats = flow_counter_utils.parse_route_flow_counter_stats(duthost) + pytest_assert(prefix_a not in stats, 'Route flow counter for {} is not removed'.format(prefix_a)) + pytest_assert(prefix_b in stats, 'Route flow counter for {} is not created'.format(prefix_b)) + + def test_max_match_count(self, rand_selected_dut): + """Test steps: + 1. Add 3 routes, set max allowed match to 2, verify only 2 route flow counters are created + 2. Remove 1 routes, verify that there are still 2 route flow counters as it should automatically fill the room + 3. Set max_allowed_match to 1, verify that there is 1 route flow counter + 4. Set max_allowed match to 2 again, verify that there are two route flow counter as it should automatically fill the room + + Args: + rand_selected_dut (object): DUT object + """ + duthost = rand_selected_dut + route_pattern = '1.1.0.0/16' + prefix_list = ['1.1.1.0/24', '1.1.2.0/24', '1.1.3.0/24'] + expect_route_flow_counter = len(prefix_list) - 1 + nexthop_addr = self._get_nexthop(duthost, False) + + with allure.step('Enable route flow counter and config route pattern to {} with max allowed {}'.format(route_pattern, expect_route_flow_counter)): + flow_counter_utils.set_route_flow_counter_status(duthost, True) + flow_counter_utils.set_route_flow_counter_pattern(duthost, route_pattern, max_match_count=expect_route_flow_counter) + + logger.info('Adding {} static routes while max allowed match count is {}'.format(len(prefix_list), expect_route_flow_counter)) + with allure.step('Adding static routes'): + for prefix in prefix_list: + add_route(rand_selected_dut, prefix, nexthop_addr) + + logger.info('Verify there are {} route flow counters'.format(expect_route_flow_counter)) + with allure.step('Verify there are {} route flow counters'.format(expect_route_flow_counter)): + stats = flow_counter_utils.parse_route_flow_counter_stats(duthost) + pytest_assert(len(stats) == expect_route_flow_counter, 'Expecetd {} route flow counters, but got {} counters'.format(expect_route_flow_counter, len(stats))) + + logger.info('Removing a route, verify there are still {} route flow counters'.format(expect_route_flow_counter)) + with allure.step('Removing a route, verify there are still {} route flow counters'.format(expect_route_flow_counter)): + del_route(rand_selected_dut, prefix_list[0], nexthop_addr) + stats = flow_counter_utils.parse_route_flow_counter_stats(duthost) + pytest_assert(len(stats) == expect_route_flow_counter, 'Max allowed match counter is {}, but got {} counters'.format(expect_route_flow_counter, len(stats))) + + expect_route_flow_counter -= 1 + logger.info('Set max_match_count to {}, verify there are {} route flow counters'.format(expect_route_flow_counter, expect_route_flow_counter)) + with allure.step('Set max_match_count to {}, verify there are {} route flow counters'.format(expect_route_flow_counter, expect_route_flow_counter)): + flow_counter_utils.set_route_flow_counter_pattern(duthost, route_pattern, max_match_count=expect_route_flow_counter) + stats = flow_counter_utils.parse_route_flow_counter_stats(duthost) + pytest_assert(len(stats) == expect_route_flow_counter, 'Max allowed match counter is {}, but got {} counters'.format(expect_route_flow_counter, len(stats))) + + expect_route_flow_counter += 1 + logger.info('Set max_match_count to {}, verify there are {} route flow counters'.format(expect_route_flow_counter, expect_route_flow_counter)) + with allure.step('Set max_match_count to {}, verify there are {} route flow counters'.format(expect_route_flow_counter, expect_route_flow_counter)): + flow_counter_utils.set_route_flow_counter_pattern(duthost, route_pattern, max_match_count=expect_route_flow_counter) + stats = flow_counter_utils.parse_route_flow_counter_stats(duthost) + pytest_assert(len(stats) == expect_route_flow_counter, 'Max allowed match counter is {}, but got {} counters'.format(expect_route_flow_counter, len(stats))) + + def _get_nexthop(self, duthost, ipv6): + """Get next hop from BGP neighbors + + Args: + duthost (object): DUT object + ipv6 (bool): True if getting IPv6 nexthop + + Returns: + str: Nexthop IP + """ + if ipv6: + cmd = 'show ipv6 bgp summary' + else: + cmd = 'show ip bgp summary' + parse_result = duthost.show_and_parse(cmd) + return parse_result[0]['neighbhor'] diff --git a/tests/route/test_static_route.py b/tests/route/test_static_route.py index bdd1e285854..55f9316b1e1 100644 --- a/tests/route/test_static_route.py +++ b/tests/route/test_static_route.py @@ -19,6 +19,7 @@ import ptf.packet as packet from pkg_resources import parse_version from tests.common import constants +from tests.flow_counter.flow_counter_utils import RouteFlowCounterTestContext, is_route_flow_counter_supported pytestmark = [ @@ -151,7 +152,7 @@ def _check_routes(): assert(wait_until(60, 15, 0, _check_routes)) -def run_static_route_test(duthost, ptfadapter, ptfhost, tbinfo, prefix, nexthop_addrs, prefix_len, nexthop_devs, nexthop_interfaces, ipv6=False, config_reload_test=False): +def run_static_route_test(duthost, ptfadapter, ptfhost, tbinfo, prefix, nexthop_addrs, prefix_len, nexthop_devs, nexthop_interfaces, is_route_flow_counter_supported, ipv6=False, config_reload_test=False): # Clean up arp or ndp clear_arp_ndp(duthost, ipv6=ipv6) @@ -165,7 +166,8 @@ def run_static_route_test(duthost, ptfadapter, ptfhost, tbinfo, prefix, nexthop_ # Check traffic get forwarded to the nexthop ip_dst = str(ipaddress.ip_network(unicode(prefix))[1]) - generate_and_verify_traffic(duthost, ptfadapter, tbinfo, ip_dst, nexthop_devs, ipv6=ipv6) + with RouteFlowCounterTestContext(is_route_flow_counter_supported, duthost, [prefix], {prefix: {'packets': '1'}}): + generate_and_verify_traffic(duthost, ptfadapter, tbinfo, ip_dst, nexthop_devs, ipv6=ipv6) # Check the route is advertised to the neighbors check_route_redistribution(duthost, prefix, ipv6) @@ -177,7 +179,8 @@ def run_static_route_test(duthost, ptfadapter, ptfhost, tbinfo, prefix, nexthop_ #FIXME: We saw re-establishing BGP sessions can takes around 7 minutes # on some devices (like 4600) after config reload, so we need below patch wait_all_bgp_up(duthost) - generate_and_verify_traffic(duthost, ptfadapter, tbinfo, ip_dst, nexthop_devs, ipv6=ipv6) + with RouteFlowCounterTestContext(is_route_flow_counter_supported, duthost, [prefix], {prefix: {'packets': '1'}}): + generate_and_verify_traffic(duthost, ptfadapter, tbinfo, ip_dst, nexthop_devs, ipv6=ipv6) check_route_redistribution(duthost, prefix, ipv6) finally: @@ -236,7 +239,7 @@ def test_static_route(rand_selected_dut, ptfadapter, ptfhost, tbinfo, toggle_all skip_201911_and_older(duthost) prefix_len, nexthop_addrs, nexthop_devs, nexthop_interfaces = get_nexthops(duthost, tbinfo) run_static_route_test(duthost, ptfadapter, ptfhost, tbinfo, "1.1.1.0/24", - nexthop_addrs, prefix_len, nexthop_devs, nexthop_interfaces) + nexthop_addrs, prefix_len, nexthop_devs, nexthop_interfaces, is_route_flow_counter_supported) @pytest.mark.disable_loganalyzer @@ -245,7 +248,7 @@ def test_static_route_ecmp(rand_selected_dut, ptfadapter, ptfhost, tbinfo, toggl skip_201911_and_older(duthost) prefix_len, nexthop_addrs, nexthop_devs, nexthop_interfaces = get_nexthops(duthost, tbinfo, count=3) run_static_route_test(duthost, ptfadapter, ptfhost, tbinfo, "2.2.2.0/24", - nexthop_addrs, prefix_len, nexthop_devs, nexthop_interfaces, config_reload_test=True) + nexthop_addrs, prefix_len, nexthop_devs, nexthop_interfaces, is_route_flow_counter_supported, config_reload_test=True) def test_static_route_ipv6(rand_selected_dut, ptfadapter, ptfhost, tbinfo, toggle_all_simulator_ports_to_rand_selected_tor_m): @@ -253,7 +256,7 @@ def test_static_route_ipv6(rand_selected_dut, ptfadapter, ptfhost, tbinfo, toggl skip_201911_and_older(duthost) prefix_len, nexthop_addrs, nexthop_devs, nexthop_interfaces = get_nexthops(duthost, tbinfo, ipv6=True) run_static_route_test(duthost, ptfadapter, ptfhost, tbinfo, "2000:1::/64", - nexthop_addrs, prefix_len, nexthop_devs, nexthop_interfaces, ipv6=True) + nexthop_addrs, prefix_len, nexthop_devs, nexthop_interfaces, is_route_flow_counter_supported, ipv6=True) @pytest.mark.disable_loganalyzer @@ -262,4 +265,4 @@ def test_static_route_ecmp_ipv6(rand_selected_dut, ptfadapter, ptfhost, tbinfo, skip_201911_and_older(duthost) prefix_len, nexthop_addrs, nexthop_devs, nexthop_interfaces = get_nexthops(duthost, tbinfo, ipv6=True, count=3) run_static_route_test(duthost, ptfadapter, ptfhost, tbinfo, "2000:2::/64", - nexthop_addrs, prefix_len, nexthop_devs, nexthop_interfaces, ipv6=True, config_reload_test=True) + nexthop_addrs, prefix_len, nexthop_devs, nexthop_interfaces, is_route_flow_counter_supported, ipv6=True, config_reload_test=True) diff --git a/tests/vxlan/test_vnet_vxlan.py b/tests/vxlan/test_vnet_vxlan.py index 56ff6597348..a4c95303fbb 100644 --- a/tests/vxlan/test_vnet_vxlan.py +++ b/tests/vxlan/test_vnet_vxlan.py @@ -14,7 +14,7 @@ from tests.common.fixtures.ptfhost_utils import remove_ip_addresses, change_mac_addresses, \ copy_arp_responder_py, copy_ptftests_directory - +from tests.flow_counter.flow_counter_utils import RouteFlowCounterTestContext, is_route_flow_counter_supported # lgtm[py/unused-import] import tests.arp.test_wr_arp as test_wr_arp from tests.common.config_reload import config_reload @@ -171,7 +171,7 @@ def is_neigh_reachable(duthost, vnet_config): return True -def test_vnet_vxlan(setup, vxlan_status, duthosts, rand_one_dut_hostname, ptfhost, vnet_test_params, creds): +def test_vnet_vxlan(setup, vxlan_status, duthosts, rand_one_dut_hostname, ptfhost, vnet_test_params, creds, is_route_flow_counter_supported): """ Test case for VNET VxLAN @@ -207,10 +207,22 @@ def test_vnet_vxlan(setup, vxlan_status, duthosts, rand_one_dut_hostname, ptfhos pytest.skip("Skip cleanup specified") logger.debug("Starting PTF runner") - ptf_runner(ptfhost, - "ptftests", - "vnet_vxlan.VNET", - platform_dir="ptftests", - params=ptf_params, - qlen=1000, - log_file=log_file) + if scenario == 'Enabled' and vxlan_enabled: + route_pattern = 'Vnet1|100.1.1.1/32' + with RouteFlowCounterTestContext(is_route_flow_counter_supported, duthost, [route_pattern], {route_pattern: {'packets': '3'}}): + ptf_runner(ptfhost, + "ptftests", + "vnet_vxlan.VNET", + platform_dir="ptftests", + params=ptf_params, + qlen=1000, + log_file=log_file) + else: + ptf_runner(ptfhost, + "ptftests", + "vnet_vxlan.VNET", + platform_dir="ptftests", + params=ptf_params, + qlen=1000, + log_file=log_file) +