Skip to content

Commit 98826f6

Browse files
Shuotian Chenglguohan
authored andcommitted
[test]: Update FIB test (#134)
- Make it work against the NG testbed - Make it work against t1 and t1-lag testbed_type Signed-off-by: Shuotian Cheng <[email protected]>
1 parent 8b34ca5 commit 98826f6

4 files changed

Lines changed: 161 additions & 220 deletions

File tree

ansible/roles/test/files/acstests/fib_test.py

Lines changed: 121 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,20 @@
1414
#---------------------------------------------------------------------
1515
# Global imports
1616
#---------------------------------------------------------------------
17-
import random
18-
import time
19-
import logging
20-
import ptf.packet as scapy
21-
import socket
22-
import ptf.dataplane as dataplane
23-
24-
from ptf.testutils import *
25-
from ptf.mask import Mask
2617
import ipaddress
27-
28-
import os
2918
import logging
30-
import unittest
19+
import random
20+
import socket
21+
import sys
3122

3223
import ptf
33-
from ptf.base_tests import BaseTest
34-
from ptf import config
24+
import ptf.packet as scapy
3525
import ptf.dataplane as dataplane
36-
import ptf.testutils as testutils
3726

38-
import pprint
27+
from ptf import config
28+
from ptf.base_tests import BaseTest
29+
from ptf.mask import Mask
30+
from ptf.testutils import *
3931

4032
class FibTest(BaseTest):
4133
'''
@@ -47,21 +39,9 @@ class FibTest(BaseTest):
4739
Routes advertized by the peers have ECMP groups. The purpose of the test is to make sure
4840
that packets are forwarded through one of the ports specified in route's ECMP group.
4941
50-
5142
This class receives a text file describing the bgp routes added to the switch.
52-
5343
File contains informaiton about each bgp route which was added to the switch.
5444
55-
#-----------------------------------------------------------------------
56-
Format of the route_info file
57-
#-----------------------------------------------------------------------
58-
Example:
59-
192.168.0.65/32 02,00,01,13,14,08,04,09,03,07,06,12,11,10,15,05,
60-
20C0:A800:0:41::/64 02,00,01,13,14,08,04,09,03,07,06,12,11,10,15,05,
61-
62-
Meaning:
63-
Each entry describes IP prefix, and indexes of ports-members of ecmp group for the route.
64-
The packet should be received from one of those ports.
6545
#-----------------------------------------------------------------------
6646
6747
The file is loaded on startup and is used to
@@ -80,22 +60,22 @@ class FibTest(BaseTest):
8060
#---------------------------------------------------------------------
8161
# Class variables
8262
#---------------------------------------------------------------------
83-
PREFIX_AND_PORT_SPLITTER=" "
84-
PORT_LIST_SPLITTER=","
85-
PORT_COUNT = 32
63+
PORT_COUNT = 32 # TODO: need to get the total number of ports from param
64+
EXPECTED_RANGE = 0.25 # TODO: need to get the percentage from param
8665

8766
'''
8867
Information about routes to test.
8968
'''
90-
route_info={}
91-
69+
port_list = [] # a list of lists describing ecmp/lag relationships
70+
route_list = [] # a list of route to be tested
71+
hit_dict = {} # a dict of hit count recording the number of hits per port
9272

9373
def __init__(self):
9474
'''
9575
@summary: constructor
9676
'''
9777
BaseTest.__init__(self)
98-
self.test_params = testutils.test_params_get()
78+
self.test_params = test_params_get()
9979
#---------------------------------------------------------------------
10080

10181
def setUp(self):
@@ -104,29 +84,25 @@ def setUp(self):
10484
'''
10585
self.dataplane = ptf.dataplane_instance
10686
self.router_mac = self.test_params['router_mac']
87+
self.load_route_info(self.test_params["route_info"])
10788
#---------------------------------------------------------------------
10889

10990
def load_route_info(self, route_info_path):
11091
'''
111-
@summary: Load route_info file into self.route_info. For details see section 'Format of the route_info file' in the summary of the class.
92+
@summary: Load route_info file
11293
@param route_info_path : Path to the file
11394
'''
11495
with open(route_info_path, 'r') as route_info_file:
115-
for line in route_info_file:
116-
line = line.strip()
117-
if (0==len(line)):
118-
continue
119-
prefix_ports_pair = line.split(self.PREFIX_AND_PORT_SPLITTER)
120-
port_list = prefix_ports_pair[1].split(self.PORT_LIST_SPLITTER)
121-
self.route_info[prefix_ports_pair[0]]=port_list
122-
return
123-
#---------------------------------------------------------------------
124-
125-
'''
126-
For diagnostic purposes only
127-
'''
128-
def print_route_info(self):
129-
pprint.pprint(self.route_info)
96+
content = route_info_file.readlines()
97+
# Parse the first line to get the port_list
98+
ecmp_list = content[0].split()
99+
for ecmp_entry in ecmp_list:
100+
lag_list = ecmp_entry.split(',')
101+
lag_list = [ int(member) for member in lag_list ]
102+
self.port_list.append(lag_list)
103+
# Parse the reset of the file to get the route_list
104+
for line in content[1:]:
105+
self.route_list.append(line.strip())
130106
return
131107
#---------------------------------------------------------------------
132108

@@ -146,7 +122,7 @@ def verify_packet_any_port(self, pkt, ports=[], device_number=0):
146122
@return: index of the port on which the packet is received and the packet.
147123
"""
148124
received = False
149-
match_index = 0
125+
match_index = -1
150126
(rcv_device, rcv_port, rcv_pkt, pkt_time) = dp_poll(
151127
self,
152128
device_number=device_number,
@@ -155,10 +131,10 @@ def verify_packet_any_port(self, pkt, ports=[], device_number=0):
155131
)
156132

157133
if rcv_port in ports:
158-
match_index = ports.index(rcv_port)
159-
received = True
134+
match_index = ports.index(rcv_port)
135+
received = True
160136

161-
return (match_index, rcv_pkt, received)
137+
return (match_index, received)
162138
#---------------------------------------------------------------------
163139

164140
def is_ipv4_address(self, ipaddr):
@@ -167,15 +143,13 @@ def is_ipv4_address(self, ipaddr):
167143
@param ipaddr IP address to check
168144
@return Boolean
169145
'''
170-
is_valid_ipv4 = True
171-
try :
146+
try:
172147
# building ipaddress fails for some of addresses unless unicode(ipaddr) is specified for both ipv4/ipv6
173148
# Example - 192.168.156.129, it is valid IPV4 address, send_packet works with it.
174-
ip = ipaddress.IPv4Address(unicode(ipaddr))
175-
except Exception, e :
176-
is_valid_ipv4 = False
177-
178-
return is_valid_ipv4
149+
ipaddress.IPv4Address(unicode(ipaddr))
150+
return True
151+
except Exception, e:
152+
return False
179153
#---------------------------------------------------------------------
180154

181155
def is_ipv6_address(self, ipaddr):
@@ -184,13 +158,11 @@ def is_ipv6_address(self, ipaddr):
184158
@param ipaddr IP address to check
185159
@return Boolean
186160
'''
187-
is_valid_ipv6 = True
188-
try :
189-
ip = ipaddress.IPv6Address(unicode(ipaddr))
161+
try:
162+
ipaddress.IPv6Address(unicode(ipaddr))
163+
return True
190164
except Exception, e:
191-
is_valid_ipv6 = False
192-
193-
return is_valid_ipv6
165+
return False
194166
#---------------------------------------------------------------------
195167

196168
def check_ipv4_route(self, source_port_index, dest_ip_addr, destination_port_list):
@@ -201,8 +173,8 @@ def check_ipv4_route(self, source_port_index, dest_ip_addr, destination_port_lis
201173
@param destination_port_list: list of ports on which to expect packet to come back from the switch
202174
@return Boolean
203175
'''
204-
sport = 0x1234
205-
dport = 0x50
176+
sport = random.randint(0, 65535)
177+
dport = random.randint(0, 65535)
206178
ip_src = "10.0.0.1"
207179
ip_dst = dest_ip_addr
208180

@@ -217,7 +189,6 @@ def check_ipv4_route(self, source_port_index, dest_ip_addr, destination_port_lis
217189
tcp_dport=dport,
218190
ip_ttl=64)
219191
exp_pkt = simple_tcp_packet(
220-
eth_dst=self.dataplane.get_mac(0, 0),
221192
eth_src=self.router_mac,
222193
ip_src=ip_src,
223194
ip_dst=ip_dst,
@@ -226,18 +197,10 @@ def check_ipv4_route(self, source_port_index, dest_ip_addr, destination_port_lis
226197
ip_ttl=63)
227198
masked_exp_pkt = Mask(exp_pkt)
228199
masked_exp_pkt.set_do_not_care_scapy(scapy.Ether,"dst")
229-
masked_exp_pkt.set_do_not_care_scapy(scapy.Ether,"src")
230200

231-
result = False
232201
send_packet(self, source_port_index, pkt)
233202

234-
(match_index,rcv_pkt, received) = self.verify_packet_any_port(masked_exp_pkt,destination_port_list)
235-
if received:
236-
result = True
237-
else:
238-
print 'FAIL for ip:%s' % dest_ip_addr ,
239-
pprint.pprint(destination_port_list)
240-
return result
203+
return self.verify_packet_any_port(masked_exp_pkt,destination_port_list)
241204
#---------------------------------------------------------------------
242205

243206
def check_ipv6_route(self, source_port_index, dest_ip_addr, destination_port_list):
@@ -248,8 +211,8 @@ def check_ipv6_route(self, source_port_index, dest_ip_addr, destination_port_lis
248211
@param destination_port_list: list of ports on which to expect packet to come back from the switch
249212
@return Boolean
250213
'''
251-
sport = 0x2233
252-
dport = 0x60
214+
sport = random.randint(0, 65535)
215+
dport = random.randint(0, 65535)
253216
ip_src = '2000::1'
254217
ip_dst = dest_ip_addr
255218

@@ -264,70 +227,106 @@ def check_ipv6_route(self, source_port_index, dest_ip_addr, destination_port_lis
264227
tcp_dport=dport,
265228
ipv6_hlim=64)
266229
exp_pkt = simple_tcpv6_packet(
267-
eth_dst=self.dataplane.get_mac(0, 0),
268-
eth_src=src_mac,
230+
eth_src=self.router_mac,
269231
ipv6_dst=ip_dst,
270232
ipv6_src=ip_src,
271233
tcp_sport=sport,
272234
tcp_dport=dport,
273235
ipv6_hlim=63)
274236
masked_exp_pkt = Mask(exp_pkt)
275237
masked_exp_pkt.set_do_not_care_scapy(scapy.Ether,"dst")
276-
masked_exp_pkt.set_do_not_care_scapy(scapy.Ether,"src")
277238

278-
result = False
279239
send_packet(self, source_port_index, pkt)
280240

281-
(match_index,rcv_pkt, received) = self.verify_packet_any_port(masked_exp_pkt,destination_port_list)
282-
283-
if received:
284-
result = True
285-
else:
286-
print 'src_port:%d' % source_port_index,
287-
print 'FAIL for ip:%s' % dest_ip_addr ,
288-
pprint.pprint(destination_port_list)
241+
return self.verify_packet_any_port(masked_exp_pkt,destination_port_list)
242+
#---------------------------------------------------------------------
243+
def check_within_expected_range(self, actual, expected):
244+
'''
245+
@summary: Check if the actual number is within the accepted range of the expected number
246+
@param actual : acutal number of recieved packets
247+
@param expected : expected number of recieved packets
248+
@return (percentage, bool)
249+
'''
250+
percentage = abs(actual - expected) / float(expected)
251+
return (percentage, percentage <= self.EXPECTED_RANGE)
252+
253+
#---------------------------------------------------------------------
254+
def check_balancing(self, port_list, port_hit_cnt):
255+
'''
256+
@summary: Check if the traffic is balanced across the ECMP groups and the LAG members
257+
@param port_list : a list of ECMP entries and in each ECMP entry a list of ports
258+
@param port_hit_cnt : a dict that records the number of packets each port received
259+
@return bool
260+
'''
261+
262+
logging.debug("%-10s \t %10s \t %10s \t %10s" % ("port(s)", "exp_cnt", "act_cnt", "diff(%)"))
263+
result = True
264+
265+
total_hit_cnt = float(sum(port_hit_cnt.values()))
266+
for ecmp_entry in port_list:
267+
total_entry_hit_cnt = 0.0
268+
for member in ecmp_entry:
269+
total_entry_hit_cnt += port_hit_cnt[member]
270+
(p, r) = self.check_within_expected_range(total_entry_hit_cnt, total_hit_cnt/len(port_list))
271+
logging.debug("%-10s \t %10d \t %10d \t %10s"
272+
% (str(ecmp_entry), total_hit_cnt/len(port_list), total_entry_hit_cnt, str(round(p, 4)*100) + '%'))
273+
result &= r
274+
for member in ecmp_entry:
275+
(p, r) = self.check_within_expected_range(port_hit_cnt[member], total_entry_hit_cnt/len(ecmp_entry))
276+
logging.debug("%-10s \t %10d \t %10d \t %10s"
277+
% (str(member), total_entry_hit_cnt/len(ecmp_entry), port_hit_cnt[member], str(round(p, 4)*100) + '%'))
278+
result &= r
289279
return result
280+
290281
#---------------------------------------------------------------------
291-
282+
292283
def runTest(self):
293284
"""
294285
@summary: Send packet for each route and validate it arrives
295286
on one of expected ECMP ports
296287
"""
297-
self.load_route_info(self.test_params["route_info"])
298-
pass_count = 0
299-
test_result = True
300-
result = True
288+
exp_port_list = []
289+
for ecmp_entry in self.port_list:
290+
for port in ecmp_entry:
291+
exp_port_list.append(port)
292+
301293
ip4_route_cnt = 0
302294
ip6_route_cnt = 0
303-
304-
for prefix, port_index_list in self.route_info.iteritems() :
305-
dest_ip_addr = prefix.split("/")[0]
306-
destination_port_list = []
307-
for port_index in port_index_list :
308-
if len(port_index) > 0 :
309-
destination_port_list.append(int(port_index))
310-
295+
ip4_hit_cnt = 0
296+
ip6_hit_cnt = 0
297+
port_cnt_dict = {}
298+
299+
x = 0
300+
for dest_ip in self.route_list:
311301
for src_port in xrange(0, self.PORT_COUNT):
312-
313-
if src_port in destination_port_list: continue
314-
315-
if self.is_ipv4_address(dest_ip_addr):
302+
if self.is_ipv4_address(dest_ip):
316303
ip4_route_cnt += 1
317-
result = self.check_ipv4_route(src_port, dest_ip_addr, destination_port_list)
318-
elif self.is_ipv6_address(dest_ip_addr):
304+
(matched_index, received) = self.check_ipv4_route(src_port, dest_ip, exp_port_list)
305+
if received:
306+
ip4_hit_cnt += 1
307+
port_cnt_dict[exp_port_list[matched_index]] = port_cnt_dict.setdefault(exp_port_list[matched_index], 0) + 1
308+
elif self.is_ipv6_address(dest_ip):
319309
ip6_route_cnt += 1
320-
result = self.check_ipv6_route(src_port, dest_ip_addr, destination_port_list)
310+
(matched_index, received) = self.check_ipv6_route(src_port, dest_ip, exp_port_list)
311+
if received:
312+
ip6_hit_cnt += 1
313+
port_cnt_dict[exp_port_list[matched_index]] = port_cnt_dict.setdefault(exp_port_list[matched_index], 0) + 1
321314
else:
322-
print 'Invalid ip address:%s' % dest_ip_addr
315+
print 'Invalid IP address:%s' % dest_ip_addr
323316
assert(False)
324317

325-
test_result = test_result and result
326-
if(result):
327-
pass_count = pass_count + 1
328-
329-
print 'pass_count:%d' % pass_count
330-
print 'ip4_route_cnt:%d' % ip4_route_cnt
331-
print 'ip6_route_cnt:%d' % ip6_route_cnt
332-
assert(test_result)
318+
ch = logging.StreamHandler(sys.stdout)
319+
# Modify the logging level to enable debugs
320+
ch.setLevel(logging.INFO)
321+
ch.terminator = ""
322+
logging.getLogger().addHandler(ch)
323+
324+
# Check if sent/received counts are matched
325+
logging.debug("\n")
326+
logging.debug("--------------------------- TEST RESULT ------------------------------")
327+
logging.debug("Sent %d IPv4 packets; recieved %d IPv4 packets" % (ip4_route_cnt, ip4_hit_cnt))
328+
logging.debug("Sent %d IPv6 packets; recieved %d IPv6 packets" % (ip6_route_cnt, ip6_hit_cnt))
329+
logging.debug("----------------------------------------------------------------------")
330+
balancing_result = self.check_balancing(self.port_list, port_cnt_dict)
331+
assert (ip4_route_cnt == ip4_hit_cnt) and (ip6_route_cnt == ip6_hit_cnt) and balancing_result
333332
#---------------------------------------------------------------------

0 commit comments

Comments
 (0)