[SDFAB-506] Add QoS test for on edge ports with GTP encapped traffic
Also, create library to attach, detach UEs via UP4 and to generate Traffic via TRex.
Change-Id: I9cd7bedbc0f799813dd033220fcdffb5baf20eda
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/trex.py b/TestON/tests/USECASE/SegmentRouting/dependencies/trex.py
new file mode 100644
index 0000000..9245f16
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/trex.py
@@ -0,0 +1,186 @@
+from distutils.util import strtobool
+from tests.USECASE.SegmentRouting.dependencies import scapy_helper
+
+
+class Trex:
+ """
+ Utility that manages interaction with TRex server via TRexDriver component
+ Example params:
+ <TREX>
+ <port_stats>0,1</port_stats>
+ <flows>
+ <RT_FROM_UE>
+ <name>Real Time</name>
+ <l1_bps>40000000</l1_bps>
+ <trex_port>0</trex_port>
+ <packet>
+ <pktlen>1400</pktlen>
+ <ip_src>10.240.0.2</ip_src>
+ <ip_dst>10.32.11.101</ip_dst>
+ <eth_src>3C:EC:EF:3E:0B:A0</eth_src>
+ <eth_dst>00:00:0A:4C:1C:46</eth_dst>
+ <gtp_teid>200</gtp_teid>
+ <s1u_addr>10.32.11.126</s1u_addr>
+ <enb_addr>10.32.11.100</enb_addr>
+ </packet>
+ <latency_stats>true</latency_stats>
+ <flow_id>10</flow_id> <!-- Mandatory when latency_stats=true -->
+ <delay>50000</delay> <!-- wait 50 ms till start to let queues fill up -->
+ <expected_min_received>1</expected_min_received>
+ <expected_max_dropped>0</expected_max_dropped>
+ <expected_max_latency>1500</expected_max_latency>
+ <expected_99_9_percentile_latency>100</expected_99_9_percentile_latency>
+ </RT_FROM_UE>
+ </flows>
+ <TREX>
+ """
+
+ def __init__(self):
+ self.trex_client = None
+ self.traffic_flows = {}
+ self.port_stats = []
+ self.packets = {} # Per-flow dictionary of packets
+
+ def setup(self, trex_client):
+ self.trex_client = trex_client
+ self.traffic_flows = main.params["TREX"]["flows"]
+ if "port_stats" in main.params["TREX"] and \
+ main.params["TREX"].get("port_stats") is not '':
+ self.port_stats = [int(p) for p in
+ main.params["TREX"].get("port_stats").split(",")]
+ self.trex_client.setupTrex(main.configPath)
+
+ def teardown(self):
+ self.trex_client.stopTrexServer()
+
+ def createFlow(self, flow_name):
+ if flow_name not in self.traffic_flows:
+ main.log.error("CFG flow not present in params")
+ return False
+ self.traffic_flows[flow_name]["packet"] = Trex.__sanitizePacketConfig(
+ self.traffic_flows[flow_name]["packet"])
+ if "gtp_teid" in self.traffic_flows[flow_name]["packet"]:
+ # packets must be GTP encapped
+ self.packets[flow_name] = scapy_helper.simple_gtp_udp_packet(
+ **self.traffic_flows[flow_name]["packet"])
+ else:
+ self.packets[flow_name] = scapy_helper.simple_udp_packet(
+ **self.traffic_flows[flow_name]["packet"])
+
+ def sendAndReceiveTraffic(self, duration):
+ """
+ Connect the client, create the flows in trex (with packets created with
+ createFlow, send and receive the traffic, and disconnect the client.
+ :param duration: traffic duration
+ :return:
+ """
+ self.trex_client.connectTrexClient()
+ for flow_name, packet in self.packets.items():
+ flow_config = self.traffic_flows[flow_name]
+ Trex.__sanitizeFlowConfig(flow_config)
+ self.trex_client.addStream(pkt=packet,
+ trex_port=flow_config["trex_port"],
+ l1_bps=flow_config["l1_bps"],
+ percentage=flow_config["percentage"],
+ delay=flow_config["delay"],
+ flow_id=flow_config["flow_id"],
+ flow_stats=flow_config["latency_stats"])
+ self.trex_client.startAndWaitTraffic(duration=duration)
+ self.trex_client.disconnectTrexClient()
+
+ def assertRxPackets(self, flow_name):
+ if not self.isFlowStats(flow_name):
+ main.log.info("No flow stats for flow {}".format(flow_name))
+ expected_min_received = int(
+ self.traffic_flows[flow_name].get("expected_min_received", "1"))
+ flow_id = self.traffic_flows[flow_name]["flow_id"]
+ flow_stats = self.trex_client.getFlowStats(flow_id)
+ utilities.assert_equals(
+ expect=True,
+ actual=flow_stats.rx_packets >= expected_min_received,
+ onpass="Traffic Flow {}: Received traffic".format(flow_name),
+ onfail="Traffic Flow {}: No traffic received".format(flow_name))
+
+ def assertDroppedPacket(self, flow_name):
+ if not self.isFlowStats(flow_name):
+ main.log.info("No flow stats for flow {}".format(flow_name))
+ expected_max_dropped = int(
+ self.traffic_flows[flow_name].get("expected_max_dropped", "0"))
+ latency_stats = self.__getLatencyStats(flow_name)
+ utilities.assert_equals(
+ expect=True,
+ actual=latency_stats.dropped <= expected_max_dropped,
+ onpass="Traffic Flow {}: {} packets dropped, below threshold ({})".format(
+ flow_name, latency_stats.dropped,
+ expected_max_dropped),
+ onfail="Traffic Flow {}: {} packets dropped, above threshold ({})".format(
+ flow_name, latency_stats.dropped,
+ expected_max_dropped))
+
+ def assertMaxLatency(self, flow_name):
+ if not self.isFlowStats(flow_name):
+ main.log.info("No flow stats for flow {}".format(flow_name))
+ expected_max_latency = int(
+ self.traffic_flows[flow_name].get("expected_max_latency", "0"))
+ latency_stats = self.__getLatencyStats(flow_name)
+ utilities.assert_equals(
+ expect=True,
+ actual=latency_stats.total_max <= expected_max_latency,
+ onpass="Traffic Flow {}: Maximum latency below threshold".format(
+ flow_name),
+ onfail="Traffic Flow {}: Maximum latency is too high {}".format(
+ flow_name, latency_stats.total_max))
+
+ def assert99_9PercentileLatency(self, flow_name):
+ if not self.isFlowStats(flow_name):
+ main.log.info("No flow stats for flow {}".format(flow_name))
+ expected_99_9_percentile_latency = int(
+ self.traffic_flows[flow_name].get(
+ "expected_99_9_percentile_latency", "0"))
+ latency_stats = self.__getLatencyStats(flow_name)
+ utilities.assert_equals(
+ expect=True,
+ actual=latency_stats.percentile_99_9 <= expected_99_9_percentile_latency,
+ onpass="Traffic Flow {}: 99.9th percentile latency below threshold".format(
+ flow_name),
+ onfail="Traffic Flow {}: 99.9th percentile latency is too high {}".format(
+ flow_name, latency_stats.percentile_99_9))
+
+ def logPortStats(self):
+ main.log.debug(self.port_stats)
+ for port in self.port_stats:
+ self.trex_client.logPortStats(port)
+
+ def logFlowStats(self, flow_name):
+ if self.isFlowStats(flow_name):
+ flow_id = self.traffic_flows[flow_name]["flow_id"]
+ self.trex_client.logFlowStats(flow_id)
+ self.trex_client.logLatencyStats(flow_id)
+
+ def isFlowStats(self, flow_name):
+ return self.traffic_flows[flow_name]["latency_stats"]
+
+ def __getLatencyStats(self, flow_name):
+ flow_id = self.traffic_flows[flow_name]["flow_id"]
+ return self.trex_client.getLatencyStats(flow_id)
+
+ @staticmethod
+ def __sanitizePacketConfig(packet):
+ if "gtp_teid" in packet.keys():
+ packet["gtp_teid"] = int(packet["gtp_teid"])
+ if "pktlen" in packet.keys():
+ packet["pktlen"] = int(packet["pktlen"])
+ return packet
+
+ @staticmethod
+ def __sanitizeFlowConfig(flow_config):
+ flow_config["trex_port"] = int(flow_config["trex_port"])
+ flow_config["percentage"] = float(
+ flow_config["percentage"]) if "percentage" in flow_config else None
+ flow_config["l1_bps"] = float(
+ flow_config["l1_bps"]) if "l1_bps" in flow_config else None
+ flow_config["delay"] = int(flow_config.get("delay", 0))
+ flow_config["flow_id"] = int(
+ flow_config["flow_id"]) if "flow_id" in flow_config else None
+ flow_config["latency_stats"] = bool(
+ strtobool(flow_config.get("latency_stats", "False")))