-
Notifications
You must be signed in to change notification settings - Fork 1k
[Buffer manager][201911]Regression test case for traditional buffer manager in 201911 #3889
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
Merged
qiluo-msft
merged 7 commits into
sonic-net:201911
from
stephenxs:201911-buffer-manager-github
Sep 27, 2021
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
80f87eb
Regression test case for traditional buffer manager in 201911
stephenxs 7c39019
Address comments and add logs
stephenxs 47dde49
Fix typo and unused import
stephenxs 3f15d26
Run traditional buffer test on Mellanox platform only
stephenxs 4c4be6b
Fix review comments
stephenxs 73e1b16
Fix a typo
stephenxs f23b323
Fix review comments
stephenxs File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,247 @@ | ||
| import logging | ||
|
|
||
| import pytest | ||
|
|
||
| from tests.common.utilities import wait_until | ||
| from tests.common.helpers.assertions import pytest_assert | ||
|
|
||
| pytestmark = [ | ||
| pytest.mark.topology('any') | ||
| ] | ||
|
|
||
| global DEFAULT_LOSSLESS_PROFILES | ||
|
|
||
| @pytest.fixture(scope="module", autouse=True) | ||
| def setup_module(duthosts, rand_one_dut_hostname): | ||
| """Setup module. Called only once when the module is initialized | ||
|
|
||
| Args: | ||
| duthosts: The duthosts object | ||
| rand_one_dut_hostname: | ||
| """ | ||
| duthost = duthosts[rand_one_dut_hostname] | ||
| if duthost.facts["asic_type"] != "mellanox": | ||
| pytest.skip("Traditional buffer test runs on Mellanox platform only, skip") | ||
|
|
||
| if "201911" not in duthost.os_version: | ||
| pytest.skip("Buffer test runs on 201911 branch only, skip") | ||
|
|
||
| load_lossless_info_from_pg_profile_lookup(duthost) | ||
|
|
||
|
|
||
| def load_lossless_info_from_pg_profile_lookup(duthost): | ||
neethajohn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """Load pg_profile_lookup.ini to a dictionary. Called only once when the module is initialized | ||
|
|
||
| Args: | ||
| duthost: the DUT host object | ||
|
|
||
| Return: | ||
| The dictionary containing the information in pg_profile_lookup.ini | ||
| """ | ||
| global DEFAULT_LOSSLESS_PROFILES | ||
|
|
||
| # Check the threshold mode | ||
| threshold_mode = duthost.shell('redis-cli -n 4 hget "BUFFER_POOL|ingress_lossless_pool" mode')['stdout'] | ||
| threshold_field_name = 'dynamic_th' if threshold_mode == 'dynamic' else 'static_th' | ||
| dut_hwsku = duthost.facts["hwsku"] | ||
| dut_platform = duthost.facts["platform"] | ||
| skudir = "/usr/share/sonic/device/{}/{}/".format(dut_platform, dut_hwsku) | ||
| pg_profile_lookup_file = os.path.join(skudir, 'pg_profile_lookup.ini') | ||
| duthost.file(path=pg_profile_lookup_file, state="file") | ||
| lines = duthost.shell('cat {}'.format(pg_profile_lookup_file))["stdout_lines"] | ||
| DEFAULT_LOSSLESS_PROFILES = {} | ||
| for line in lines: | ||
| if line[0] == '#': | ||
| continue | ||
| tokens = line.split() | ||
| speed = tokens[0] | ||
| cable_length = tokens[1] | ||
| size = tokens[2] | ||
| xon = tokens[3] | ||
| xoff = tokens[4] | ||
| threshold = tokens[5] | ||
| profile_info = { | ||
| 'pool': '[BUFFER_POOL|ingress_lossless_pool]', | ||
| 'size': size, | ||
| 'xon': xon, | ||
| 'xoff': xoff, | ||
| threshold_field_name: threshold} | ||
| if len(tokens) > 6: | ||
| profile_info['xon_offset'] = tokens[6] | ||
| DEFAULT_LOSSLESS_PROFILES[(speed, cable_length)] = profile_info | ||
|
|
||
|
|
||
| def make_dict_from_output_lines(lines): | ||
| if lines: | ||
| return dict(zip(lines[::2], lines[1::2])) | ||
| return None | ||
|
|
||
|
|
||
| def test_buffer_pg(duthosts, rand_one_dut_hostname, conn_graph_facts): | ||
| """The testcase for (traditional) buffer manager | ||
|
|
||
| 1. For all ports in the config_db, | ||
| - Check whether there is no lossless buffer PG configured on an admin-down port | ||
| - Check whether the lossless PG aligns with the port's speed and cable length | ||
| - If name to oid maps exist for port and PG, check whether the information in ASIC_DB aligns with that in CONFIG_DB | ||
| - If a lossless profile hasn't been checked, check whether lossless profile in CONFIG_DB aligns with | ||
| - pg_profile_lookup.ini according to speed and cable length | ||
| - information in ASIC_DB | ||
| 2. Shutdown a port and check whether the lossless buffer PG has been removed | ||
| 3. Startup the port and check whether the lossless PG has been readded. | ||
| """ | ||
| def _check_condition(condition, message, use_assert): | ||
| """Check whether the condition is satisfied | ||
|
|
||
| Args: | ||
| condition: The condition to check | ||
| message: The message to log or in pytest_assert | ||
| use_assert: Whether to use assert or not. If this is called from wait_until(), it should be False. | ||
|
|
||
| Return: | ||
| The condition | ||
| """ | ||
| if use_assert: | ||
| pytest_assert(condition, message) | ||
| elif not condition: | ||
| logging.info("Port buffer check: {}".format(message)) | ||
| return False | ||
|
|
||
| return True | ||
|
|
||
| def _check_port_buffer_info_and_get_profile_oid(duthost, port, expected_profile, use_assert=True): | ||
| """Check port's buffer information against CONFIG_DB and ASIC_DB | ||
|
|
||
| Args: | ||
| duthost: The duthost object | ||
| port: The port to test in string | ||
| expected_profile: The expected profile in string | ||
| use_assert: Whether or not to use pytest_assert in case any conditional check isn't satisfied | ||
|
|
||
| Return: | ||
| A tuple consisting of the OID of buffer profile and whether there is any check failed | ||
| """ | ||
| profile_in_pg = duthost.shell('redis-cli -n 4 hget "BUFFER_PG|{}|3-4" profile'.format(port))['stdout'] | ||
| buffer_profile_oid = None | ||
| default_lossless_pgs = ['3', '4'] | ||
|
|
||
| if expected_profile: | ||
| if not _check_condition(profile_in_pg == expected_profile, "Buffer profile of lossless PG of port {} isn't the expected ({})".format(port, expected_profile), use_assert): | ||
| return None, False | ||
|
|
||
| if pg_name_map: | ||
| for pg in default_lossless_pgs: | ||
| buffer_pg_asic_oid = pg_name_map['{}:{}'.format(port, pg)] | ||
| buffer_pg_asic_key = duthost.shell('redis-cli -n 1 keys *{}*'.format(buffer_pg_asic_oid))['stdout'] | ||
| buffer_profile_oid_in_pg = duthost.shell('redis-cli -n 1 hget {} SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE'.format(buffer_pg_asic_key))['stdout'] | ||
| logging.info("Checking admin-up port {} lossless PG {} in ASIC_DB ({})".format(port, pg, buffer_profile_oid_in_pg)) | ||
| if buffer_profile_oid: | ||
| if not _check_condition(buffer_profile_oid == buffer_profile_oid_in_pg, | ||
| "Different OIDs in PG 3 ({}) and 4 ({}) in port {}".format(buffer_profile_oid, buffer_profile_oid_in_pg, port), | ||
| use_assert): | ||
| return None, False | ||
| else: | ||
| buffer_profile_oid = buffer_profile_oid_in_pg | ||
| else: | ||
| if not _check_condition(not profile_in_pg, "Buffer PG configured on admin down port {}".format(port), use_assert): | ||
| return None, False | ||
| if pg_name_map: | ||
| for pg in default_lossless_pgs: | ||
| buffer_pg_asic_oid = pg_name_map['{}:{}'.format(port, pg)] | ||
| buffer_pg_asic_key = duthost.shell('redis-cli -n 1 keys *{}*'.format(buffer_pg_asic_oid))['stdout'] | ||
| buffer_profile_oid_in_pg = duthost.shell('redis-cli -n 1 hget {} SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE'.format(buffer_pg_asic_key))['stdout'] | ||
| logging.info("Checking admin-down port {} lossless PG {}".format(port, pg)) | ||
| if not _check_condition(not buffer_profile_oid_in_pg or buffer_profile_oid_in_pg == 'oid:0x0', | ||
| "Buffer PG configured on admin down port in ASIC_DB {}".format(port), | ||
| use_assert): | ||
| return None, False | ||
|
|
||
| return buffer_profile_oid, True | ||
|
|
||
| def _check_port_buffer_info_and_return(duthost, port, expected_profile): | ||
| """Check port's buffer information against CONFIG_DB and ASIC_DB and return the result | ||
|
|
||
| This is called from wait_until | ||
|
|
||
| Args: | ||
| duthost: The duthost object | ||
| port: The port to test in string | ||
| expected_profile: The expected profile in string | ||
|
|
||
| Return: | ||
| Whether all the checks passed | ||
| """ | ||
| _, result = _check_port_buffer_info_and_get_profile_oid(duthost, port, expected_profile, False) | ||
| return result | ||
|
|
||
| global DEFAULT_LOSSLESS_PROFILES | ||
|
|
||
| duthost = duthosts[rand_one_dut_hostname] | ||
|
|
||
| # Check whether the COUNTERS_PG_NAME_MAP exists. Skip ASIC_DB checking if it isn't | ||
| pg_name_map = make_dict_from_output_lines(duthost.shell('redis-cli -n 2 hgetall COUNTERS_PG_NAME_MAP')['stdout'].split()) | ||
| cable_length_map = make_dict_from_output_lines(duthost.shell('redis-cli -n 4 hgetall "CABLE_LENGTH|AZURE"')['stdout'].split()) | ||
|
|
||
| configdb_ports = [x.split('|')[1] for x in duthost.shell('redis-cli -n 4 keys "PORT|*"')['stdout'].split()] | ||
| profiles_checked = {} | ||
| lossless_pool_oid = None | ||
| admin_up_ports = set() | ||
| for port in configdb_ports: | ||
| port_config = make_dict_from_output_lines(duthost.shell('redis-cli -n 4 hgetall "PORT|{}"'.format(port))['stdout'].split()) | ||
|
|
||
| if port_config.get('admin_status') == 'up': | ||
| admin_up_ports.add(port) | ||
| cable_length = cable_length_map[port] | ||
| speed = port_config['speed'] | ||
| expected_profile = '[BUFFER_PROFILE|pg_lossless_{}_{}_profile]'.format(speed, cable_length) | ||
|
|
||
| logging.info("Checking admin-up port {} buffer information: profile {}".format(port, expected_profile)) | ||
|
|
||
| buffer_profile_oid, _ = _check_port_buffer_info_and_get_profile_oid(duthost, port, expected_profile) | ||
|
|
||
| if expected_profile not in profiles_checked: | ||
| profile_info = make_dict_from_output_lines(duthost.shell('redis-cli -n 4 hgetall "{}"'.format(expected_profile[1:-1]))['stdout'].split()) | ||
| pytest_assert(profile_info == DEFAULT_LOSSLESS_PROFILES[(speed, cable_length)], "Buffer profile {} {} doesn't match default {}".format(expected_profile, profile_info, DEFAULT_LOSSLESS_PROFILES[(speed, cable_length)])) | ||
|
|
||
| logging.info("Checking buffer profile {}: OID: {}".format(expected_profile, buffer_profile_oid)) | ||
| if buffer_profile_oid: | ||
| # Further check the buffer profile in ASIC_DB | ||
| buffer_profile_key = duthost.shell('redis-cli -n 1 keys *{}*'.format(buffer_profile_oid))['stdout'] | ||
| buffer_profile_asic_info = make_dict_from_output_lines(duthost.shell('redis-cli -n 1 hgetall {}'.format(buffer_profile_key))['stdout'].split()) | ||
| pytest_assert(buffer_profile_asic_info['SAI_BUFFER_PROFILE_ATTR_XON_TH'] == profile_info['xon'] and | ||
| buffer_profile_asic_info['SAI_BUFFER_PROFILE_ATTR_XOFF_TH'] == profile_info['xoff'] and | ||
| buffer_profile_asic_info['SAI_BUFFER_PROFILE_ATTR_RESERVED_BUFFER_SIZE'] == profile_info['size'] and | ||
| (buffer_profile_asic_info['SAI_BUFFER_PROFILE_ATTR_THRESHOLD_MODE'] == 'SAI_BUFFER_PROFILE_THRESHOLD_MODE_DYNAMIC' and | ||
| buffer_profile_asic_info['SAI_BUFFER_PROFILE_ATTR_SHARED_DYNAMIC_TH'] == profile_info['dynamic_th'] or | ||
| buffer_profile_asic_info['SAI_BUFFER_PROFILE_ATTR_THRESHOLD_MODE'] == 'SAI_BUFFER_PROFILE_THRESHOLD_MODE_STATIC' and | ||
| buffer_profile_asic_info['SAI_BUFFER_PROFILE_ATTR_SHARED_STATIC_TH'] == profile_info['static_th']), | ||
| "Buffer profile {} {} doesn't align with ASIC_TABLE {}".format(expected_profile, profile_info, buffer_profile_asic_info)) | ||
|
|
||
| profiles_checked[expected_profile] = buffer_profile_oid | ||
| if not lossless_pool_oid: | ||
| lossless_pool_oid = buffer_profile_asic_info['SAI_BUFFER_PROFILE_ATTR_POOL_ID'] | ||
| else: | ||
| pytest_assert(lossless_pool_oid == buffer_profile_asic_info['SAI_BUFFER_PROFILE_ATTR_POOL_ID'], | ||
| "Buffer profile {} has different buffer pool id {} from others {}".format(expected_profile, buffer_profile_asic_info['SAI_BUFFER_PROFILE_ATTR_POOL_ID'], lossless_pool_oid)) | ||
| else: | ||
| pytest_assert(profiles_checked[expected_profile] == buffer_profile_oid, | ||
| "PG {}|3-4 has different OID of profile from other PGs sharing the same profile {}".format(port, expected_profile)) | ||
| else: | ||
| # Port admin down. Make sure no lossless PG configured. | ||
| logging.info("Checking admin-down port buffer information: {}".format(port)) | ||
| _, _ = _check_port_buffer_info_and_get_profile_oid(duthost, port, None) | ||
|
|
||
| port_to_shutdown = admin_up_ports.pop() | ||
| expected_profile = duthost.shell('redis-cli -n 4 hget "BUFFER_PG|{}|3-4" profile'.format(port_to_shutdown))['stdout'] | ||
| try: | ||
| # Shutdown the port and check whether the lossless PG has been removed | ||
| logging.info("Shut down an admin-up port {} and check its buffer information".format(port_to_shutdown)) | ||
| duthost.shell('config interface shutdown {}'.format(port_to_shutdown)) | ||
| wait_until(60, 5, _check_port_buffer_info_and_return, duthost, port_to_shutdown, None) | ||
|
|
||
| # Startup the port and check whether the lossless PG has been reconfigured | ||
| logging.info("Re-startup the port {} and check its buffer information".format(port_to_shutdown)) | ||
| duthost.shell('config interface startup {}'.format(port_to_shutdown)) | ||
| wait_until(60, 5, _check_port_buffer_info_and_return, duthost, port_to_shutdown, expected_profile) | ||
| finally: | ||
| duthost.shell('config interface startup {}'.format(port_to_shutdown), module_ignore_errors=True) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.