-
Notifications
You must be signed in to change notification settings - Fork 1k
[inner hashing] extend inner hashing test with dynamic Policy Base Hashing configurations #4078
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 7 commits
93c7315
ad2e616
c1aa30c
b2b82f5
c0f39a6
62464c8
ffc4b50
70261e2
cac7489
67a7413
a299230
b4eed52
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,326 @@ | ||
| import time | ||
| import json | ||
| import logging | ||
| import tempfile | ||
|
|
||
| from datetime import datetime | ||
|
|
||
| import pytest | ||
|
|
||
| from tests.common.fixtures.ptfhost_utils import copy_ptftests_directory, change_mac_addresses # lgtm[py/unused-import] | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| # Standard HASH_KEYs of 'src-ip', 'dst-ip', 'src-port', 'dst-port', 'ip-proto' varied in the inner packets sent and used to validate hashing | ||
| # outer-tuples is also used as a HASH_KEY to validate that varying any outer tuples for encap traffic does not affect inner hashing | ||
| HASH_KEYS = ['src-ip', 'dst-ip', 'src-port', 'dst-port', 'ip-proto', 'outer-tuples'] | ||
| SRC_IP_RANGE = ['8.0.0.0', '8.255.255.255'] | ||
| DST_IP_RANGE = ['9.0.0.0', '9.255.255.255'] | ||
| SRC_IPV6_RANGE = ['20D0:A800:0:00::', '20D0:A800:0:00::FFFF:FFFF'] | ||
| DST_IPV6_RANGE = ['20D0:A800:0:01::', '20D0:A800:0:01::FFFF:FFFF'] | ||
| PTF_QLEN = 2000 | ||
|
|
||
| PTF_TEST_PORT_MAP = '/root/ptf_test_port_map.json' | ||
| FIB_INFO_FILE_DST = '/root/fib_info.txt' | ||
|
|
||
| VXLAN_PORT = 13330 | ||
| DUT_VXLAN_PORT_JSON_FILE = '/tmp/vxlan.switch.json' | ||
|
|
||
| IP_VERSIONS_LIST = ["ipv4", "ipv6"] | ||
| OUTER_ENCAP_FORMATS = ["vxlan", "nvgre"] | ||
| TABLE_NAME = "pbh_table" | ||
| TABLE_DESCRIPTION = "NVGRE and VXLAN" | ||
| HASH_NAME = "inner_hash" | ||
| VXLAN_RULE_NAME = "vxlan_{}_{}" | ||
| NVGRE_RULE_NAME = "nvgre_{}_{}" | ||
| VXLAN_RULE_PRIO = "1" | ||
| NVGRE_RULE_PRIO = "2" | ||
| ECMP_PACKET_ACTION = "SET_ECMP_HASH" | ||
| V4_ETHER_TYPE = "0x0800" | ||
| V6_ETHER_TYPE = "0x86dd" | ||
| VXLAN_IP_PROTOCOL = "0x11" | ||
| NVGRE_IP_PROTOCOL = "0x2f" | ||
| VXLAN_L4_DST_PORT = "0x3412" | ||
| VXLAN_L4_DST_PORT_OPTION = " --l4-dst-port {}".format(VXLAN_L4_DST_PORT) | ||
| ADD_PBH_TABLE_CMD = "sudo config pbh table add '{}' --interface-list '{}' --description '{}'" | ||
| DEL_PBH_TABLE_CMD = "sudo config pbh table delete '{}'" | ||
| ADD_PBH_RULE_BASE_CMD = "sudo config pbh rule add '{}' '{}' --priority '{}' --ether-type {}" \ | ||
| " --inner-ether-type '{}' --hash '{}' --packet-action '{}' --flow-counter 'ENABLED'" | ||
AntonHryshchuk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| DEL_PBH_RULE_CMD = "sudo config pbh rule delete '{}' '{}'" | ||
| ADD_PBH_HASH_CMD = "sudo config pbh hash add '{}' --hash-field-list '{}'" | ||
| DEL_PBH_HASH_CMD = "sudo config pbh hash delete '{}'" | ||
| ADD_PBH_HASH_FIELD_CMD = "sudo config pbh hash-field add '{}' --hash-field '{}' --sequence-id '{}'" | ||
| DEL_PBH_HASH_FIELD_CMD = "sudo config pbh hash-field delete '{}'" | ||
|
|
||
| PBH_HASH_FIELD_LIST = "inner_ip_proto," \ | ||
| "inner_l4_dst_port,inner_l4_src_port," \ | ||
| "inner_dst_ipv4,inner_src_ipv4," \ | ||
| "inner_src_ipv6,inner_dst_ipv6" | ||
| HASH_FIELD_CONFIG = { | ||
| "inner_ip_proto": {"field": "INNER_IP_PROTOCOL", "sequence": "1"}, | ||
| "inner_l4_dst_port": {"field": "INNER_L4_DST_PORT", "sequence": "2"}, | ||
| "inner_l4_src_port": {"field": "INNER_L4_SRC_PORT", "sequence": "2"}, | ||
| "inner_src_ipv4": {"field": "INNER_SRC_IPV4", "sequence": "3", "mask": "255.255.255.255"}, | ||
| "inner_dst_ipv4": {"field": "INNER_DST_IPV4", "sequence": "3", "mask": "255.255.255.255"}, | ||
| "inner_src_ipv6": {"field": "INNER_SRC_IPV6", "sequence": "4", "mask": "::ffff:ffff"}, | ||
| "inner_dst_ipv6": {"field": "INNER_DST_IPV6", "sequence": "4", "mask": "::ffff:ffff"} | ||
| } | ||
|
|
||
|
|
||
| @pytest.fixture(scope='module') | ||
| def config_facts(duthosts, rand_one_dut_hostname): | ||
| duthost = duthosts[rand_one_dut_hostname] | ||
| return duthost.config_facts(host=duthost.hostname, source='running')['ansible_facts'] | ||
|
|
||
|
|
||
| @pytest.fixture(scope='module', autouse=True) | ||
| def setup(duthosts, rand_one_dut_hostname): | ||
| duthost = duthosts[rand_one_dut_hostname] | ||
|
|
||
| vxlan_switch_config = [{ | ||
| "SWITCH_TABLE:switch": { | ||
| "vxlan_port": VXLAN_PORT | ||
| }, | ||
| "OP": "SET" | ||
| }] | ||
|
|
||
| logger.info("Copying vxlan.switch.json with data: " + str(vxlan_switch_config)) | ||
|
|
||
| duthost.copy(content=json.dumps(vxlan_switch_config, indent=4), dest=DUT_VXLAN_PORT_JSON_FILE) | ||
| duthost.shell("docker cp {} swss:/vxlan.switch.json".format(DUT_VXLAN_PORT_JSON_FILE)) | ||
| duthost.shell("docker exec swss sh -c \"swssconfig /vxlan.switch.json\"") | ||
| time.sleep(3) | ||
|
|
||
|
|
||
| @pytest.fixture(scope='module', autouse=True) | ||
| def build_fib(duthosts, rand_one_dut_hostname, ptfhost, config_facts, tbinfo): | ||
| duthost = duthosts[rand_one_dut_hostname] | ||
|
|
||
| timestamp = datetime.now().strftime('%Y-%m-%d-%H:%M:%S') | ||
|
|
||
| mg_facts = duthost.get_extended_minigraph_facts(tbinfo) | ||
|
|
||
| duthost.shell("redis-dump -d 0 -k 'ROUTE*' -y > /tmp/fib.{}.txt".format(timestamp)) | ||
| duthost.fetch(src="/tmp/fib.{}.txt".format(timestamp), dest="/tmp/fib") | ||
|
|
||
| po = config_facts.get('PORTCHANNEL', {}) | ||
| ports = config_facts.get('PORT', {}) | ||
|
|
||
| tmp_fib_info = tempfile.NamedTemporaryFile() | ||
| with open("/tmp/fib/{}/tmp/fib.{}.txt".format(duthost.hostname, timestamp)) as fp: | ||
| fib = json.load(fp) | ||
| for k, v in fib.items(): | ||
| skip = False | ||
| prefix = k.split(':', 1)[1] | ||
| ifnames = v['value']['ifname'].split(',') | ||
| nh = v['value']['nexthop'] | ||
|
|
||
| oports = [] | ||
| for ifname in ifnames: | ||
| if po.has_key(ifname): | ||
| oports.append([str(mg_facts['minigraph_ptf_indices'][x]) for x in po[ifname]['members']]) | ||
| else: | ||
| if ports.has_key(ifname): | ||
| oports.append([str(mg_facts['minigraph_ptf_indices'][ifname])]) | ||
| else: | ||
| logger.info("Route point to non front panel port {}:{}".format(k, v)) | ||
| skip = True | ||
| # skip direct attached subnet | ||
| if nh == '0.0.0.0' or nh == '::' or nh == "": | ||
| skip = True | ||
|
|
||
| if not skip: | ||
| tmp_fib_info.write("{}".format(prefix)) | ||
| for op in oports: | ||
| tmp_fib_info.write(" [{}]".format(" ".join(op))) | ||
| tmp_fib_info.write("\n") | ||
| else: | ||
| tmp_fib_info.write("{} []\n".format(prefix)) | ||
| tmp_fib_info.flush() | ||
|
|
||
| ptfhost.copy(src=tmp_fib_info.name, dest=FIB_INFO_FILE_DST) | ||
| msg = "Copied FIB info to PTF host '{}': local_path={}, remote_path={}" | ||
| logger.info(msg.format(ptfhost.hostname, tmp_fib_info.name, FIB_INFO_FILE_DST)) | ||
|
|
||
| tmp_fib_info.close() | ||
|
|
||
|
|
||
| @pytest.fixture(scope='module') | ||
| def vlan_ptf_ports(config_facts, tbinfo, duthost): | ||
| ports = [] | ||
| mg_facts = duthost.get_extended_minigraph_facts(tbinfo) | ||
| for vlan_members in config_facts.get('VLAN_MEMBER', {}).values(): | ||
| for intf in vlan_members.keys(): | ||
| dut_port_index = mg_facts['minigraph_ptf_indices'][intf] | ||
| logging.info("Added " + str(dut_port_index)) | ||
| ports.append(dut_port_index) | ||
|
|
||
| return ports | ||
|
|
||
|
|
||
| @pytest.fixture(scope='module') | ||
| def router_mac(duthosts, rand_one_dut_hostname): | ||
| duthost = duthosts[rand_one_dut_hostname] | ||
| return duthost.facts['router_mac'] | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
| def hash_keys(): | ||
| hash_keys = HASH_KEYS[:] | ||
| return hash_keys | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
| def symmetric_hashing(duthosts, rand_one_dut_hostname): | ||
| duthost = duthosts[rand_one_dut_hostname] | ||
| symmetric_hashing = False | ||
|
|
||
| if duthost.facts['asic_type'] in ["mellanox"]: | ||
| symmetric_hashing = True | ||
|
|
||
| return symmetric_hashing | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module", params=IP_VERSIONS_LIST) | ||
| def outer_ipver(request): | ||
| return request.param | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module", params=IP_VERSIONS_LIST) | ||
| def inner_ipver(request): | ||
| return request.param | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module", autouse=True) | ||
| def setup_dynamic_pbh(request): | ||
| request.getfixturevalue("config_pbh_table") | ||
| request.getfixturevalue("config_hash_fields") | ||
| request.getfixturevalue("config_hash") | ||
| request.getfixturevalue("config_rules") | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
| def config_pbh_table(duthost, vlan_ptf_ports, tbinfo): | ||
| test_intfs_str = get_dut_test_intfs_str(duthost, vlan_ptf_ports, tbinfo) | ||
|
|
||
| duthost.command(ADD_PBH_TABLE_CMD.format(TABLE_NAME, | ||
| test_intfs_str, | ||
| TABLE_DESCRIPTION)) | ||
|
|
||
| yield | ||
|
|
||
| duthost.command(DEL_PBH_TABLE_CMD.format(TABLE_NAME)) | ||
|
|
||
|
|
||
| def get_dut_test_intfs_str(duthost, vlan_ptf_ports, tbinfo): | ||
| test_intfs = [] | ||
| # get ports according to chosen ptf ports indices | ||
| mg_facts = duthost.get_extended_minigraph_facts(tbinfo) | ||
| for intf, index in mg_facts['minigraph_ptf_indices'].items(): | ||
| if index in vlan_ptf_ports: | ||
| test_intfs.append(intf) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like we may need validation to check if this works with other interface types like portchannels, Ethernet interface for comprehensive test coverage
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test is dedicated to t0 topology. To use other interface types, need to update the test to t1-lag topology(as an example).
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No not necessary, you can take ports out of the vlan and configure them in a static portchannel or Ethernet interface and use that as the src port too.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case, it's a static(custom) config. Need to run the static test. This part of the code is related to dynamic config only. Or maybe I don't quite understand what you mean.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I mean we can modify the switch config here in the dynamic test to cover the other L3 interface types like portchannel interface and validate hashing with that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I understand, it's mean to change the topology configurations especially for the test, and not use the base configurations |
||
| return ",".join(test_intfs) | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
| def config_hash_fields(duthost): | ||
| for hash_field, hash_field_params_dict in HASH_FIELD_CONFIG.items(): | ||
| cmd = get_hash_field_add_cmd(hash_field, hash_field_params_dict) | ||
| duthost.command(cmd) | ||
|
|
||
| yield | ||
|
|
||
| for hash_field in HASH_FIELD_CONFIG.keys(): | ||
| duthost.command(DEL_PBH_HASH_FIELD_CMD.format(hash_field)) | ||
|
|
||
|
|
||
| def get_hash_field_add_cmd(hash_field_name, hash_field_params_dict): | ||
| cmd = ADD_PBH_HASH_FIELD_CMD.format(hash_field_name, | ||
| hash_field_params_dict["field"], | ||
| hash_field_params_dict["sequence"]) | ||
| if "mask" in hash_field_params_dict: | ||
| cmd += " --ip-mask '{}'".format(hash_field_params_dict["mask"]) | ||
| return cmd | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
| def config_hash(duthost): | ||
| duthost.command(ADD_PBH_HASH_CMD.format(HASH_NAME, PBH_HASH_FIELD_LIST)) | ||
|
|
||
| yield | ||
|
|
||
| duthost.command(DEL_PBH_HASH_CMD.format(HASH_NAME)) | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
| def config_rules(duthost): | ||
| for ipver in IP_VERSIONS_LIST: | ||
AntonHryshchuk marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| config_ipv4_rules(duthost, ipver) | ||
| config_ipv6_rules(duthost, ipver) | ||
|
|
||
| yield | ||
|
|
||
| for ipver in IP_VERSIONS_LIST: | ||
| delete_ipv4_rules(duthost, ipver) | ||
| delete_ipv6_rules(duthost, ipver) | ||
|
|
||
|
|
||
| def config_ipv4_rules(duthost, inner_ipver): | ||
| config_vxlan_rule(duthost, " --ip-protocol {}", V4_ETHER_TYPE, "ipv4", inner_ipver) | ||
| config_nvgre_rule(duthost, " --ip-protocol {}", V4_ETHER_TYPE, "ipv4", inner_ipver) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like we maybe lacking test coverage for matching on: So would suggest adding a 3rd random packet format with a gre_key match, that way we can get full test coverage.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will be covered in the next PR |
||
|
|
||
|
|
||
| def config_ipv6_rules(duthost, inner_ipver): | ||
| config_vxlan_rule(duthost, " --ipv6-next-header {}", V6_ETHER_TYPE, "ipv6", inner_ipver) | ||
| config_nvgre_rule(duthost, " --ipv6-next-header {}", V6_ETHER_TYPE, "ipv6", inner_ipver) | ||
|
|
||
|
|
||
| def config_vxlan_rule(duthost, ip_ver_option, ether_type, outer_ipver, inner_ipver): | ||
| inner_ether_type = V4_ETHER_TYPE if inner_ipver == "ipv4" else V6_ETHER_TYPE | ||
| duthost.command((ADD_PBH_RULE_BASE_CMD + ip_ver_option + VXLAN_L4_DST_PORT_OPTION) | ||
| .format(TABLE_NAME, | ||
| VXLAN_RULE_NAME.format(outer_ipver, inner_ipver), | ||
| VXLAN_RULE_PRIO, | ||
| ether_type, | ||
| inner_ether_type, | ||
| HASH_NAME, | ||
| ECMP_PACKET_ACTION, | ||
| VXLAN_IP_PROTOCOL, | ||
| VXLAN_L4_DST_PORT)) | ||
|
|
||
|
|
||
| def config_nvgre_rule(duthost, ip_ver_option, ether_type, outer_ipver, inner_ipver): | ||
| inner_ether_type = V4_ETHER_TYPE if inner_ipver == "ipv4" else V6_ETHER_TYPE | ||
| duthost.command((ADD_PBH_RULE_BASE_CMD + ip_ver_option) | ||
| .format(TABLE_NAME, | ||
| NVGRE_RULE_NAME.format(outer_ipver, inner_ipver), | ||
| NVGRE_RULE_PRIO, | ||
| ether_type, | ||
| inner_ether_type, | ||
| HASH_NAME, | ||
| ECMP_PACKET_ACTION, | ||
| NVGRE_IP_PROTOCOL)) | ||
|
|
||
|
|
||
| def delete_ipv4_rules(duthost, inner_ipver): | ||
| delete_vxlan_nvgre_rules(duthost, "ipv4", inner_ipver) | ||
|
|
||
|
|
||
| def delete_ipv6_rules(duthost, inner_ipver): | ||
| delete_vxlan_nvgre_rules(duthost, "ipv6", inner_ipver) | ||
|
|
||
|
|
||
| def delete_vxlan_nvgre_rules(duthost, outer_ipver, inner_ipver): | ||
| duthost.command(DEL_PBH_RULE_CMD.format(TABLE_NAME, VXLAN_RULE_NAME.format(outer_ipver, inner_ipver))) | ||
| duthost.command(DEL_PBH_RULE_CMD.format(TABLE_NAME, NVGRE_RULE_NAME.format(outer_ipver, inner_ipver))) | ||
|
|
||
|
|
||
| def get_src_dst_ip_range(ipver): | ||
| if ipver == "ipv4": | ||
| src_ip_range = SRC_IP_RANGE | ||
| dst_ip_range = DST_IP_RANGE | ||
| else: | ||
| src_ip_range = SRC_IPV6_RANGE | ||
| dst_ip_range = DST_IPV6_RANGE | ||
| return src_ip_range, dst_ip_range | ||
Uh oh!
There was an error while loading. Please reload this page.