Skip to content
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

[pytest/hash] Rewrite hash test #1506

Merged
merged 2 commits into from
Apr 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 35 additions & 89 deletions ansible/roles/test/files/ptftests/hash_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,9 @@ def setUp(self):

self.src_ip_range = [unicode(x) for x in self.test_params['src_ip_range'].split(',')]
self.dst_ip_range = [unicode(x) for x in self.test_params['dst_ip_range'].split(',')]
self.test_hash_srcip = self.test_params.get('hash_srcip', True)
self.test_hash_dstip = self.test_params.get('hash_dstip', True)
self.test_hash_srcport = self.test_params.get('hash_srcport', True)
self.test_hash_dstport = self.test_params.get('hash_dstport', True)
self.test_hash_inport = self.test_params.get('hash_inport', False)
self.test_ipv4 = self.test_params.get('ipv4', True)
self.test_ipv6 = self.test_params.get('ipv6', True)
self.src_ip_interval = lpm.LpmDict.IpInterval(ip_address(self.src_ip_range[0]), ip_address(self.src_ip_range[1]))
self.dst_ip_interval = lpm.LpmDict.IpInterval(ip_address(self.dst_ip_range[0]), ip_address(self.dst_ip_range[1]))
self.hash_keys = self.test_params.get('hash_keys', ['src-ip', 'dst-ip', 'src-port', 'dst-port'])

self.balancing_range = self.test_params.get('balancing_range', self.DEFAULT_BALANCING_RANGE)

Expand All @@ -77,19 +73,8 @@ def setUp(self):
self.src_ports = range(0, 120)
#---------------------------------------------------------------------

def check_hash(self, src_ip_range, dst_ip_range, ipv4=True):

src_ip_interval = lpm.LpmDict.IpInterval(ip_address(src_ip_range[0]), ip_address(src_ip_range[1]))
dst_ip_interval = lpm.LpmDict.IpInterval(ip_address(dst_ip_range[0]), ip_address(dst_ip_range[1]))

# hash field for regular packets:
# src_ip, dst_ip, protocol, l4_src_port, l4_dst_port (if applicable)

# initialize all parameters
src_ip = src_ip_interval.get_random_ip()
dst_ip = dst_ip_interval.get_random_ip()
src_port = random.randint(0, 65535)
dst_port = random.randint(0, 65535)
def check_hash(self, hash_key):
dst_ip = self.dst_ip_interval.get_random_ip()
next_hop = self.fib[dst_ip]
exp_port_list = self.fib[dst_ip].get_next_hop_list()
logging.info("exp_port_list: {}".format(exp_port_list))
Expand All @@ -98,63 +83,19 @@ def check_hash(self, src_ip_range, dst_ip_range, ipv4=True):
assert False
in_port = random.choice([port for port in self.src_ports if port not in exp_port_list])

### check hash fields ###

hit_count_map = {}
# step 1: check randomizing source ip
if self.test_hash_srcip:
for i in range(0, self.BALANCING_TEST_TIMES):
src_ip = src_ip_interval.get_random_ip()
(matched_index, _) = self.check_ip_route(
in_port, src_port, dst_port, src_ip, dst_ip, exp_port_list, ipv4)
hit_count_map[matched_index] = hit_count_map.get(matched_index, 0) + 1
logging.info("hit count map: {}".format(hit_count_map))
self.check_balancing(next_hop.get_next_hop(), hit_count_map)

# step 2: check randomizing destination ip
if self.test_hash_dstip:
hit_count_map.clear()
for i in range(0, self.BALANCING_TEST_TIMES):
dst_ip = dst_ip_interval.get_random_ip()
(matched_index, _) = self.check_ip_route(
in_port, src_port, dst_port, src_ip, dst_ip, exp_port_list, ipv4)
hit_count_map[matched_index] = hit_count_map.get(matched_index, 0) + 1
logging.info("hit count map: {}".format(hit_count_map))
self.check_balancing(next_hop.get_next_hop(), hit_count_map)

# step 3: check randomizing l3 source port
if self.test_hash_srcport:
hit_count_map.clear()
for i in range(0, self.BALANCING_TEST_TIMES):
src_port = random.randint(0, 65535)
(matched_index, _) = self.check_ip_route(
in_port, src_port, dst_port, src_ip, dst_ip, exp_port_list, ipv4)
hit_count_map[matched_index] = hit_count_map.get(matched_index, 0) + 1
logging.info("hit count map: {}".format(hit_count_map))
self.check_balancing(next_hop.get_next_hop(), hit_count_map)

# step 4: check randomizing l4 destination port
if self.test_hash_dstport:
hit_count_map.clear()
for i in range(0, self.BALANCING_TEST_TIMES):
dst_port = random.randint(0, 65535)
(matched_index, _) = self.check_ip_route(
in_port, src_port, dst_port, src_ip, dst_ip, exp_port_list, ipv4)
hit_count_map[matched_index] = hit_count_map.get(matched_index, 0) + 1
logging.info("hit count map: {}".format(hit_count_map))
self.check_balancing(exp_port_list, hit_count_map)

# step 5: check randomizing in port
# TODO

def check_ip_route(self, in_port, sport, dport, src_ip_addr, dst_ip_addr,
dst_port_list, ipv4=True):
if ipv4:
(matched_index, received) = self.check_ipv4_route(in_port, sport, dport,
src_ip_addr, dst_ip_addr, dst_port_list)
for _ in range(0, self.BALANCING_TEST_TIMES):
logging.info("in_port: {}".format(in_port))
(matched_index, _) = self.check_ip_route(hash_key, in_port, dst_ip, exp_port_list)
hit_count_map[matched_index] = hit_count_map.get(matched_index, 0) + 1
logging.info("hit count map: {}".format(hit_count_map))
self.check_balancing(next_hop.get_next_hop(), hit_count_map)

def check_ip_route(self, hash_key, in_port, dst_ip, dst_port_list):
if ip_network(unicode(dst_ip)).version == 4:
(matched_index, received) = self.check_ipv4_route(hash_key, in_port, dst_port_list)
else:
(matched_index, received) = self.check_ipv6_route(in_port, sport, dport,
src_ip_addr, dst_ip_addr, dst_port_list)
(matched_index, received) = self.check_ipv6_route(hash_key, in_port, dst_port_list)

assert received

Expand All @@ -163,15 +104,18 @@ def check_ip_route(self, in_port, sport, dport, src_ip_addr, dst_ip_addr,

return (matched_port, received)

def check_ipv4_route(self, in_port, sport, dport,
ip_src, ip_dst, dst_port_list):
def check_ipv4_route(self, hash_key, in_port, dst_port_list):
'''
@summary: Check IPv4 route works.
@param hash_key: hash key to build packet with.
@param in_port: index of port to use for sending packet to switch
@param dest_ip_addr: destination IP to build packet with.
@param dst_port_list: list of ports on which to expect packet to come back from the switch
'''
src_mac = self.dataplane.get_mac(0, 0)
ip_src = self.src_ip_interval.get_random_ip() if hash_key == 'src-ip' else self.src_ip_interval.get_first_ip()
ip_dst = self.dst_ip_interval.get_random_ip() if hash_key == 'dst-ip' else self.dst_ip_interval.get_first_ip()
sport = random.randint(0, 65535) if hash_key == 'src-port' else 1234
dport = random.randint(0, 65535) if hash_key == 'dst-port' else 80

pkt = simple_tcp_packet(
eth_dst=self.router_mac,
Expand All @@ -197,16 +141,19 @@ def check_ipv4_route(self, in_port, sport, dport,
return verify_packet_any_port(self, masked_exp_pkt, dst_port_list)
#---------------------------------------------------------------------

def check_ipv6_route(self, in_port, sport, dport,
ip_src, ip_dst, dst_port_list):
def check_ipv6_route(self, hash_key, in_port, dst_port_list):
'''
@summary: Check IPv6 route works.
@param source_port_index: index of port to use for sending packet to switch
@param dest_ip_addr: destination IP to build packet with.
@param hash_key: hash key to build packet with.
@param in_port: index of port to use for sending packet to switch
@param dst_port_list: list of ports on which to expect packet to come back from the switch
@return Boolean
'''
src_mac = self.dataplane.get_mac(0, 0)
ip_src = self.src_ip_interval.get_random_ip() if hash_key == 'src-ip' else self.src_ip_interval.get_first_ip()
ip_dst = self.dst_ip_interval.get_random_ip() if hash_key == 'dst-ip' else self.dst_ip_interval.get_first_ip()
sport = random.randint(0, 65535) if hash_key == 'src-port' else 1234
dport = random.randint(0, 65535) if hash_key == 'dst-port' else 80

pkt = simple_tcpv6_packet(
eth_dst=self.router_mac,
Expand Down Expand Up @@ -260,14 +207,14 @@ def check_balancing(self, dest_port_list, port_hit_cnt):
total_entry_hit_cnt += port_hit_cnt.get(member, 0)
(p, r) = self.check_within_expected_range(total_entry_hit_cnt, float(total_hit_cnt)/len(dest_port_list))
logging.info("%-10s \t %-10s \t %10d \t %10d \t %10s"
% ("ECMP", str(ecmp_entry), total_hit_cnt/len(dest_port_list), total_entry_hit_cnt, str(round(p, 4)*100) + '%'))
% ("ECMP", str(ecmp_entry), total_hit_cnt/len(dest_port_list), total_entry_hit_cnt, str(round(p, 4)*100) + '%'))
result &= r
if len(ecmp_entry) == 1 or total_entry_hit_cnt == 0:
continue
for member in ecmp_entry:
(p, r) = self.check_within_expected_range(port_hit_cnt.get(member, 0), float(total_entry_hit_cnt)/len(ecmp_entry))
logging.info("%-10s \t %-10s \t %10d \t %10d \t %10s"
% ("LAG", str(member), total_entry_hit_cnt/len(ecmp_entry), port_hit_cnt.get(member, 0), str(round(p, 4)*100) + '%'))
% ("LAG", str(member), total_entry_hit_cnt/len(ecmp_entry), port_hit_cnt.get(member, 0), str(round(p, 4)*100) + '%'))
result &= r

assert result
Expand All @@ -279,8 +226,7 @@ def runTest(self):
@summary: Send packet for each range of both IPv4 and IPv6 spaces and
expect the packet to be received from one of the expected ports
"""
# IPv4 Test
if (self.test_ipv4):
self.check_hash(self.src_ip_range, self.dst_ip_range)
if (self.test_ipv6):
self.check_hash(self.src_ip_range, self.dst_ip_range, ipv4=False)

for hash_key in self.hash_keys:
logging.info("hash test hash_key: {}".format(hash_key))
self.check_hash(hash_key)
90 changes: 50 additions & 40 deletions tests/fib/test_fib.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,20 @@

logger = logging.getLogger(__name__)

HASH_KEYS = ['src-ip', 'dst-ip', 'src-port', 'dst-port', 'ingress-port']
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']
DST_IPV6_RANGE = ['20D0:A800:0:01::', '20D0:A800:0:01::FFFF']

g_vars = {}

def build_fib(duthost, config_facts, fibfile, t):

mg_facts = duthost.minigraph_facts(host=duthost.hostname)['ansible_facts']

duthost.shell("redis-dump -d 0 -k 'ROUTE*' -y > /tmp/fib.{}.txt".format(t))
res = duthost.fetch(src="/tmp/fib.{}.txt".format(t), dest="/tmp/fib")
duthost.fetch(src="/tmp/fib.{}.txt".format(t), dest="/tmp/fib")

po = config_facts.get('PORTCHANNEL', {})
ports = config_facts.get('PORT', {})
Expand Down Expand Up @@ -93,70 +101,72 @@ def test_fib(testbed, duthost, ptfhost, ipv4, ipv6, mtu):
log_file=log_file,
socket_recv_size=16384)

@pytest.mark.parametrize("ipv4, ipv6", [pytest.param(True, True)])
def test_hash(testbed, duthost, ptfhost, ipv4, ipv6):

config_facts = duthost.config_facts(host=duthost.hostname, source="running")['ansible_facts']

class TestHash():
hash_keys = HASH_KEYS
t = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')

ofpname = "/tmp/fib/{}/tmp/fib_info.{}.txt".format(duthost.hostname, t)
@pytest.fixture(scope="class", autouse=True)
def setup_hash(self, testbed, duthost, ptfhost):
global g_vars

build_fib(duthost, config_facts, ofpname, t)
config_facts = duthost.config_facts(host=duthost.hostname, source="running")['ansible_facts']

ptfhost.copy(src=ofpname, dest="/root/fib_info.txt")
ptfhost.copy(src="ptftests", dest="/root")
logging.info("run ptf test")
ofpname = "/tmp/fib/{}/tmp/fib_info.{}.txt".format(duthost.hostname, self.t)

# do not test load balancing on L4 port on vs platform as kernel 4.9
# can only do load balance base on L3
hash_srcport = True
hash_dstport = True
meta = config_facts.get('DEVICE_METADATA')
if meta['localhost']['platform'] == 'x86_64-kvm_x86_64-r0':
hash_srcport = False
hash_dstport = False
build_fib(duthost, config_facts, ofpname, self.t)

testbed_type = testbed['topo']['name']
router_mac = duthost.shell('sonic-cfggen -d -v \'DEVICE_METADATA.localhost.mac\'')["stdout_lines"][0].decode("utf-8")
ptfhost.copy(src=ofpname, dest="/root/fib_info.txt")
ptfhost.copy(src="ptftests", dest="/root")
logging.info("run ptf test")

# do not test load balancing on L4 port on vs platform as kernel 4.9
# can only do load balance base on L3
meta = config_facts.get('DEVICE_METADATA')
if meta['localhost']['platform'] == 'x86_64-kvm_x86_64-r0':
self.hash_keys.remove('src-port')
self.hash_keys.remove('dst-port')

# TODO
self.hash_keys.remove('ingress-port')

g_vars['testbed_type'] = testbed['topo']['name']
g_vars['router_mac'] = duthost.shell('sonic-cfggen -d -v \'DEVICE_METADATA.localhost.mac\'')["stdout_lines"][0].decode("utf-8")

if ipv4:
log_file = "/tmp/hash_test.HashTest.ipv4.{}.ipv6.{}.{}.log".format(True, False, t)
def test_hash_ipv4(self, ptfhost):
log_file = "/tmp/hash_test.HashTest.ipv4.{}.log".format(self.t)
logging.info("PTF log file: %s" % log_file)

src_ip_range = ['8.0.0.0', '9.0.0.0']
dst_ip_range = ['8.0.0.0', '9.0.0.0']
src_ip_range = SRC_IP_RANGE
dst_ip_range = DST_IP_RANGE

ptf_runner(ptfhost,
"ptftests",
"hash_test.HashTest",
platform_dir="ptftests",
params={"testbed_type": testbed_type,
"router_mac": router_mac,
params={"testbed_type": g_vars['testbed_type'],
"router_mac": g_vars['router_mac'],
"fib_info": "/root/fib_info.txt",
"src_ip_range": ",".join(src_ip_range),
"dst_ip_range": ",".join(dst_ip_range),
"hash_srcport": hash_srcport,
"hash_dstport": hash_dstport,
"ipv4": True,
"ipv6": False },
"hash_keys": self.hash_keys },
log_file=log_file,
socket_recv_size=16384)
if ipv6:
log_file = "/tmp/hash_test.HashTest.ipv4.{}.ipv6.{}.{}.log".format(False, True, t)

def test_hash_ipv6(self, ptfhost):
log_file = "/tmp/hash_test.HashTest.ipv6.{}.log".format(self.t)
logging.info("PTF log file: %s" % log_file)

src_ip_range = ['20D0:A800:0:00::', '20D0:A800:0:00::FFFF']
dst_ip_range = ['20D0:A800:0:00::', '20D0:A800:0:00::FFFF']
src_ip_range = SRC_IPV6_RANGE
dst_ip_range = DST_IPV6_RANGE

ptf_runner(ptfhost,
"ptftests",
"hash_test.HashTest",
platform_dir="ptftests",
params={"testbed_type": testbed_type,
"router_mac": router_mac,
params={"testbed_type": g_vars['testbed_type'],
"router_mac": g_vars['router_mac'],
"fib_info": "/root/fib_info.txt",
"src_ip_range": ",".join(src_ip_range),
"dst_ip_range": ",".join(dst_ip_range),
"ipv4": False,
"ipv6": True },
"hash_keys": self.hash_keys },
log_file=log_file,
socket_recv_size=16384)