blob: 93a7c707f16952121c0c34d762153724aece1c67 [file] [log] [blame]
Daniele Moro80889562021-09-08 10:09:26 +02001from distutils.util import strtobool
2from tests.USECASE.SegmentRouting.dependencies import scapy_helper
3
4
5class Trex:
6 """
7 Utility that manages interaction with TRex server via TRexDriver component
8 Example params:
9 <TREX>
10 <port_stats>0,1</port_stats>
11 <flows>
12 <RT_FROM_UE>
13 <name>Real Time</name>
14 <l1_bps>40000000</l1_bps>
15 <trex_port>0</trex_port>
16 <packet>
17 <pktlen>1400</pktlen>
18 <ip_src>10.240.0.2</ip_src>
19 <ip_dst>10.32.11.101</ip_dst>
20 <eth_src>3C:EC:EF:3E:0B:A0</eth_src>
21 <eth_dst>00:00:0A:4C:1C:46</eth_dst>
22 <gtp_teid>200</gtp_teid>
23 <s1u_addr>10.32.11.126</s1u_addr>
24 <enb_addr>10.32.11.100</enb_addr>
25 </packet>
26 <latency_stats>true</latency_stats>
27 <flow_id>10</flow_id> <!-- Mandatory when latency_stats=true -->
28 <delay>50000</delay> <!-- wait 50 ms till start to let queues fill up -->
29 <expected_min_received>1</expected_min_received>
30 <expected_max_dropped>0</expected_max_dropped>
31 <expected_max_latency>1500</expected_max_latency>
32 <expected_99_9_percentile_latency>100</expected_99_9_percentile_latency>
33 </RT_FROM_UE>
34 </flows>
35 <TREX>
36 """
37
38 def __init__(self):
39 self.trex_client = None
40 self.traffic_flows = {}
41 self.port_stats = []
42 self.packets = {} # Per-flow dictionary of packets
43
44 def setup(self, trex_client):
45 self.trex_client = trex_client
46 self.traffic_flows = main.params["TREX"]["flows"]
47 if "port_stats" in main.params["TREX"] and \
48 main.params["TREX"].get("port_stats") is not '':
49 self.port_stats = [int(p) for p in
50 main.params["TREX"].get("port_stats").split(",")]
51 self.trex_client.setupTrex(main.configPath)
52
53 def teardown(self):
54 self.trex_client.stopTrexServer()
55
56 def createFlow(self, flow_name):
57 if flow_name not in self.traffic_flows:
58 main.log.error("CFG flow not present in params")
59 return False
60 self.traffic_flows[flow_name]["packet"] = Trex.__sanitizePacketConfig(
61 self.traffic_flows[flow_name]["packet"])
62 if "gtp_teid" in self.traffic_flows[flow_name]["packet"]:
63 # packets must be GTP encapped
64 self.packets[flow_name] = scapy_helper.simple_gtp_udp_packet(
65 **self.traffic_flows[flow_name]["packet"])
66 else:
67 self.packets[flow_name] = scapy_helper.simple_udp_packet(
68 **self.traffic_flows[flow_name]["packet"])
69
Yi Tsengdda7e322021-09-20 14:21:20 -070070 def resetFlows(self):
71 self.packets = {}
72
Daniele Moro80889562021-09-08 10:09:26 +020073 def sendAndReceiveTraffic(self, duration):
74 """
75 Connect the client, create the flows in trex (with packets created with
76 createFlow, send and receive the traffic, and disconnect the client.
77 :param duration: traffic duration
Daniele Morof811f9f2021-09-21 19:07:52 +020078 :return: port statistics collected while running the test
Daniele Moro80889562021-09-08 10:09:26 +020079 """
80 self.trex_client.connectTrexClient()
81 for flow_name, packet in self.packets.items():
82 flow_config = self.traffic_flows[flow_name]
83 Trex.__sanitizeFlowConfig(flow_config)
84 self.trex_client.addStream(pkt=packet,
85 trex_port=flow_config["trex_port"],
86 l1_bps=flow_config["l1_bps"],
87 percentage=flow_config["percentage"],
88 delay=flow_config["delay"],
89 flow_id=flow_config["flow_id"],
90 flow_stats=flow_config["latency_stats"])
Daniele Morof811f9f2021-09-21 19:07:52 +020091 result = self.trex_client.startAndWaitTraffic(duration=duration,
92 ports=self.port_stats)
Daniele Moro80889562021-09-08 10:09:26 +020093 self.trex_client.disconnectTrexClient()
Daniele Morof811f9f2021-09-21 19:07:52 +020094 return result
95
96 def verifyCongestion(self, live_stats, multiplier=1):
97 """
98 Verify and assert that the test was able to generate congestion by
99 checking that average TX traffic is greater than average RX traffic from
100 stats collected during the test.
101
102 :param live_stats: Stats collected during tests
103 :param multiplier: Multiplier for RX traffic in case we encap/decap traffic
104 :return:
105 """
106 avg_tx = sum(
107 [sum(v["tx_bps"]) / len(v["tx_bps"])
108 for (k, v) in live_stats.items() if k != "duration"]
109 )
110 avg_rx = sum(
111 [sum(v["rx_bps"]) / len(v["rx_bps"])
112 for (k, v) in live_stats.items() if k != "duration"]
113 )
114
115 utilities.assert_equals(
116 expect=True,
117 actual=avg_tx > avg_rx * multiplier,
118 onpass="Congestion created: AVG TX ({}) > AVG RX ({})".format(
119 avg_tx, avg_rx),
120 onfail="NO Congestion: AVG TX ({}) <= AVG RX ({})".format(
121 avg_tx, avg_rx)
122 )
Daniele Moro80889562021-09-08 10:09:26 +0200123
124 def assertRxPackets(self, flow_name):
125 if not self.isFlowStats(flow_name):
126 main.log.info("No flow stats for flow {}".format(flow_name))
127 expected_min_received = int(
128 self.traffic_flows[flow_name].get("expected_min_received", "1"))
129 flow_id = self.traffic_flows[flow_name]["flow_id"]
130 flow_stats = self.trex_client.getFlowStats(flow_id)
131 utilities.assert_equals(
132 expect=True,
133 actual=flow_stats.rx_packets >= expected_min_received,
134 onpass="Traffic Flow {}: Received traffic".format(flow_name),
135 onfail="Traffic Flow {}: No traffic received".format(flow_name))
136
137 def assertDroppedPacket(self, flow_name):
138 if not self.isFlowStats(flow_name):
139 main.log.info("No flow stats for flow {}".format(flow_name))
140 expected_max_dropped = int(
141 self.traffic_flows[flow_name].get("expected_max_dropped", "0"))
142 latency_stats = self.__getLatencyStats(flow_name)
143 utilities.assert_equals(
144 expect=True,
145 actual=latency_stats.dropped <= expected_max_dropped,
146 onpass="Traffic Flow {}: {} packets dropped, below threshold ({})".format(
147 flow_name, latency_stats.dropped,
148 expected_max_dropped),
149 onfail="Traffic Flow {}: {} packets dropped, above threshold ({})".format(
150 flow_name, latency_stats.dropped,
151 expected_max_dropped))
152
153 def assertMaxLatency(self, flow_name):
154 if not self.isFlowStats(flow_name):
155 main.log.info("No flow stats for flow {}".format(flow_name))
156 expected_max_latency = int(
157 self.traffic_flows[flow_name].get("expected_max_latency", "0"))
158 latency_stats = self.__getLatencyStats(flow_name)
159 utilities.assert_equals(
160 expect=True,
161 actual=latency_stats.total_max <= expected_max_latency,
162 onpass="Traffic Flow {}: Maximum latency below threshold".format(
163 flow_name),
164 onfail="Traffic Flow {}: Maximum latency is too high {}".format(
165 flow_name, latency_stats.total_max))
166
167 def assert99_9PercentileLatency(self, flow_name):
168 if not self.isFlowStats(flow_name):
169 main.log.info("No flow stats for flow {}".format(flow_name))
Daniele Morof811f9f2021-09-21 19:07:52 +0200170 return
171 if not "expected_99_9_percentile_latency" in self.traffic_flows[flow_name].keys():
172 main.log.info("No 99.9th percentile parameter for test")
173 return
Daniele Moro80889562021-09-08 10:09:26 +0200174 expected_99_9_percentile_latency = int(
175 self.traffic_flows[flow_name].get(
176 "expected_99_9_percentile_latency", "0"))
177 latency_stats = self.__getLatencyStats(flow_name)
178 utilities.assert_equals(
179 expect=True,
180 actual=latency_stats.percentile_99_9 <= expected_99_9_percentile_latency,
181 onpass="Traffic Flow {}: 99.9th percentile latency below threshold".format(
182 flow_name),
183 onfail="Traffic Flow {}: 99.9th percentile latency is too high {}".format(
184 flow_name, latency_stats.percentile_99_9))
185
Daniele Morof811f9f2021-09-21 19:07:52 +0200186 def assert90PercentileLatency(self, flow_name):
187 if not self.isFlowStats(flow_name):
188 main.log.info("No flow stats for flow {}".format(flow_name))
189 return
190 if not "expected_90_percentile_latency" in self.traffic_flows[flow_name].keys():
191 main.log.info("No 90th percentile parameter for test")
192 return
193 expected_90_percentile_latency = int(
194 self.traffic_flows[flow_name].get(
195 "expected_90_percentile_latency", "0"))
196 latency_stats = self.__getLatencyStats(flow_name)
197 utilities.assert_equals(
198 expect=True,
199 actual=latency_stats.percentile_90 <= expected_90_percentile_latency,
200 onpass="Traffic Flow {}: 90th percentile latency below threshold".format(
201 flow_name),
202 onfail="Traffic Flow {}: 90th percentile latency is too high {}".format(
203 flow_name, latency_stats.percentile_90))
204
Daniele Moro80889562021-09-08 10:09:26 +0200205 def logPortStats(self):
206 main.log.debug(self.port_stats)
207 for port in self.port_stats:
208 self.trex_client.logPortStats(port)
209
210 def logFlowStats(self, flow_name):
211 if self.isFlowStats(flow_name):
212 flow_id = self.traffic_flows[flow_name]["flow_id"]
213 self.trex_client.logFlowStats(flow_id)
214 self.trex_client.logLatencyStats(flow_id)
215
216 def isFlowStats(self, flow_name):
217 return self.traffic_flows[flow_name]["latency_stats"]
218
219 def __getLatencyStats(self, flow_name):
220 flow_id = self.traffic_flows[flow_name]["flow_id"]
221 return self.trex_client.getLatencyStats(flow_id)
222
223 @staticmethod
224 def __sanitizePacketConfig(packet):
225 if "gtp_teid" in packet.keys():
226 packet["gtp_teid"] = int(packet["gtp_teid"])
227 if "pktlen" in packet.keys():
228 packet["pktlen"] = int(packet["pktlen"])
Daniele Moro04a62d12021-10-06 17:37:36 +0200229 if "udp_dport" in packet.keys():
230 packet["udp_dport"] = int(packet["udp_dport"])
231 if "udp_sport" in packet.keys():
232 packet["udp_sport"] = int(packet["udp_sport"])
Daniele Moro80889562021-09-08 10:09:26 +0200233 return packet
234
235 @staticmethod
236 def __sanitizeFlowConfig(flow_config):
237 flow_config["trex_port"] = int(flow_config["trex_port"])
238 flow_config["percentage"] = float(
239 flow_config["percentage"]) if "percentage" in flow_config else None
240 flow_config["l1_bps"] = float(
241 flow_config["l1_bps"]) if "l1_bps" in flow_config else None
242 flow_config["delay"] = int(flow_config.get("delay", 0))
243 flow_config["flow_id"] = int(
244 flow_config["flow_id"]) if "flow_id" in flow_config else None
245 flow_config["latency_stats"] = bool(
246 strtobool(flow_config.get("latency_stats", "False")))