diff options
Diffstat (limited to 'nfvbench/traffic_gen/trex.py')
-rw-r--r-- | nfvbench/traffic_gen/trex.py | 93 |
1 files changed, 61 insertions, 32 deletions
diff --git a/nfvbench/traffic_gen/trex.py b/nfvbench/traffic_gen/trex.py index 23faebc..cabf1cb 100644 --- a/nfvbench/traffic_gen/trex.py +++ b/nfvbench/traffic_gen/trex.py @@ -48,6 +48,8 @@ from trex_stl_lib.api import STLVmFlowVarRepetableRandom from trex_stl_lib.api import STLVmWrFlowVar from trex_stl_lib.api import UDP from trex_stl_lib.services.trex_stl_service_arp import STLServiceARP + + # pylint: enable=import-error @@ -64,31 +66,42 @@ class TRex(AbstractTrafficGenerator): self.streamblock = defaultdict(list) self.rates = [] self.arps = {} + self.capture_id = None + self.packet_list = [] def get_version(self): return self.client.get_server_version() def extract_stats(self, in_stats): + """Extract stats from dict returned by Trex API. + + :param in_stats: dict as returned by TRex api + """ utils.nan_replace(in_stats) LOG.debug(in_stats) result = {} + # port_handles should have only 2 elements: [0, 1] + # so (1 - ph) will be the index for the far end port for ph in self.port_handle: - stats = self.__combine_stats(in_stats, ph) + stats = in_stats[ph] + far_end_stats = in_stats[1 - ph] result[ph] = { 'tx': { - 'total_pkts': cast_integer(stats['tx_pkts']['total']), - 'total_pkt_bytes': cast_integer(stats['tx_bytes']['total']), - 'pkt_rate': cast_integer(stats['tx_pps']['total']), - 'pkt_bit_rate': cast_integer(stats['tx_bps']['total']) + 'total_pkts': cast_integer(stats['opackets']), + 'total_pkt_bytes': cast_integer(stats['obytes']), + 'pkt_rate': cast_integer(stats['tx_pps']), + 'pkt_bit_rate': cast_integer(stats['tx_bps']) }, 'rx': { - 'total_pkts': cast_integer(stats['rx_pkts']['total']), - 'total_pkt_bytes': cast_integer(stats['rx_bytes']['total']), - 'pkt_rate': cast_integer(stats['rx_pps']['total']), - 'pkt_bit_rate': cast_integer(stats['rx_bps']['total']), + 'total_pkts': cast_integer(stats['ipackets']), + 'total_pkt_bytes': cast_integer(stats['ibytes']), + 'pkt_rate': cast_integer(stats['rx_pps']), + 'pkt_bit_rate': cast_integer(stats['rx_bps']), + # how many pkts were dropped in RX direction + # need to take the tx counter on the far end port 'dropped_pkts': cast_integer( - stats['tx_pkts']['total'] - stats['rx_pkts']['total']) + far_end_stats['opackets'] - stats['ipackets']) } } @@ -103,20 +116,6 @@ class TRex(AbstractTrafficGenerator): result["total_tx_rate"] = cast_integer(total_tx_pkts / self.config.duration_sec) return result - def __combine_stats(self, in_stats, port_handle): - """Traverses TRex result dictionary and combines stream stats. Used for combining latency - and regular streams together. - """ - result = defaultdict(lambda: defaultdict(float)) - - for pg_id in [self.stream_ids[port_handle]] + self.latencies[port_handle]: - record = in_stats['flow_stats'][pg_id] - for stat_type, stat_type_values in record.iteritems(): - for ph, value in stat_type_values.iteritems(): - result[stat_type][ph] += value - - return result - def __combine_latencies(self, in_stats, port_handle): """Traverses TRex result dictionary and combines chosen latency stats.""" if not self.latencies[port_handle]: @@ -136,14 +135,16 @@ class TRex(AbstractTrafficGenerator): return result def create_pkt(self, stream_cfg, l2frame_size): - # 46 = 14 (Ethernet II) + 4 (CRC Checksum) + 20 (IPv4) + 8 (UDP) - payload = 'x' * (max(64, int(l2frame_size)) - 46) pkt_base = Ether(src=stream_cfg['mac_src'], dst=stream_cfg['mac_dst']) - if stream_cfg['vlan_tag'] is not None: + # 50 = 14 (Ethernet II) + 4 (Vlan tag) + 4 (CRC Checksum) + 20 (IPv4) + 8 (UDP) pkt_base /= Dot1Q(vlan=stream_cfg['vlan_tag']) - + l2payload_size = int(l2frame_size) - 50 + else: + # 46 = 14 (Ethernet II) + 4 (CRC Checksum) + 20 (IPv4) + 8 (UDP) + l2payload_size = int(l2frame_size) - 46 + payload = 'x' * l2payload_size udp_args = {} if stream_cfg['udp_src_port']: udp_args['sport'] = int(stream_cfg['udp_src_port']) @@ -198,6 +199,8 @@ class TRex(AbstractTrafficGenerator): idx_lat = None streams = [] if l2frame == 'IMIX': + min_size = 64 if stream_cfg['vlan_tag'] is None else 68 + self.adjust_imix_min_size(min_size) for t, (ratio, l2_frame_size) in enumerate(zip(self.imix_ratios, self.imix_l2_sizes)): pkt = self.create_pkt(stream_cfg, l2_frame_size) streams.append(STLStream(packet=pkt, @@ -208,6 +211,7 @@ class TRex(AbstractTrafficGenerator): if latency: idx_lat = self.id.next() + pkt = self.create_pkt(stream_cfg, self.imix_avg_l2_size) sl = STLStream(packet=pkt, isg=isg, flow_stats=STLFlowLatencyStats(pg_id=idx_lat), @@ -247,7 +251,7 @@ class TRex(AbstractTrafficGenerator): break except Exception as ex: if it == (self.config.generic_retry_count - 1): - raise ex + raise LOG.info("Retrying connection to TRex (%s)...", ex.message) def connect(self): @@ -318,7 +322,7 @@ class TRex(AbstractTrafficGenerator): def __start_server(self): server = TRexTrafficServer() - server.run_server(self.config.generator_config) + server.run_server(self.config.generator_config, self.config.vlan_tagging) def resolve_arp(self): self.client.set_service_mode(ports=self.port_handle) @@ -354,7 +358,7 @@ class TRex(AbstractTrafficGenerator): else: failed = [arp.get_record().dst_ip for arp in arps if arp.get_record().dst_mac is None] - LOG.info('Retrying ARP for: %d (%d / %d)', + LOG.info('Retrying ARP for: %s (%d / %d)', failed, attempt, self.config.generic_retry_count) time.sleep(self.config.generic_poll_sec) @@ -433,7 +437,7 @@ class TRex(AbstractTrafficGenerator): LOG.info('Cleared all existing streams.') def get_stats(self): - stats = self.client.get_pgid_stats() + stats = self.client.get_stats() return self.extract_stats(stats) def get_macs(self): @@ -450,6 +454,31 @@ class TRex(AbstractTrafficGenerator): def stop_traffic(self): self.client.stop(ports=self.port_handle) + def start_capture(self): + """Capture all packets on both ports that are unicast to us.""" + if self.capture_id: + self.stop_capture() + # Need to filter out unwanted packets so we do not end up counting + # src MACs of frames that are not unicast to us + src_mac_list = self.get_macs() + bpf_filter = "ether dst %s or ether dst %s" % (src_mac_list[0], src_mac_list[1]) + # ports must be set in service in order to enable capture + self.client.set_service_mode(ports=self.port_handle) + self.capture_id = self.client.start_capture(rx_ports=self.port_handle, + bpf_filter=bpf_filter) + + def fetch_capture_packets(self): + if self.capture_id: + self.packet_list = [] + self.client.fetch_capture_packets(capture_id=self.capture_id['id'], + output=self.packet_list) + + def stop_capture(self): + if self.capture_id: + self.client.stop_capture(capture_id=self.capture_id['id']) + self.capture_id = None + self.client.set_service_mode(ports=self.port_handle, enabled=False) + def cleanup(self): if self.client: try: |