X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=nfvbench%2Ftraffic_gen%2Ftrex_gen.py;h=af70cdea1e7da837b938a78e1680b9bd48d556e5;hb=423f360415e2834dd8de065434023b822e2ca3f8;hp=989940afafdd968aa3e768e5b0fcb46c6dbc8b89;hpb=abe283a31d38c2c44824d720bf18b47c5443b97a;p=nfvbench.git diff --git a/nfvbench/traffic_gen/trex_gen.py b/nfvbench/traffic_gen/trex_gen.py index 989940a..af70cde 100644 --- a/nfvbench/traffic_gen/trex_gen.py +++ b/nfvbench/traffic_gen/trex_gen.py @@ -20,17 +20,14 @@ import time import traceback from itertools import count +# pylint: disable=import-error +from scapy.contrib.mpls import MPLS # flake8: noqa +# pylint: enable=import-error from nfvbench.log import LOG from nfvbench.traffic_server import TRexTrafficServer from nfvbench.utils import cast_integer from nfvbench.utils import timeout from nfvbench.utils import TimeoutError -from traffic_base import AbstractTrafficGenerator -from traffic_base import TrafficGeneratorException -import traffic_utils as utils -from traffic_utils import IMIX_AVG_L2_FRAME_SIZE -from traffic_utils import IMIX_L2_SIZES -from traffic_utils import IMIX_RATIOS # pylint: disable=import-error from trex.common.services.trex_service_arp import ServiceARP @@ -57,9 +54,15 @@ from trex.stl.api import ThreeBytesField from trex.stl.api import UDP from trex.stl.api import XByteField - # pylint: enable=import-error +from .traffic_base import AbstractTrafficGenerator +from .traffic_base import TrafficGeneratorException +from . import traffic_utils as utils +from .traffic_utils import IMIX_AVG_L2_FRAME_SIZE +from .traffic_utils import IMIX_L2_SIZES +from .traffic_utils import IMIX_RATIOS + class VXLAN(Packet): """VxLAN class.""" @@ -245,11 +248,74 @@ class TRex(AbstractTrafficGenerator): else: latencies[port].min_usec = get_latency(lat['total_min']) latencies[port].avg_usec = get_latency(lat['average']) + # pick up the HDR histogram if present (otherwise will raise KeyError) + latencies[port].hdrh = lat['hdrh'] except KeyError: pass def __combine_latencies(self, in_stats, results, port_handle): - """Traverse TRex result dictionary and combines chosen latency stats.""" + """Traverse TRex result dictionary and combines chosen latency stats. + + example of latency dict returned by trex (2 chains): + 'latency': {256: {'err_cntrs': {'dropped': 0, + 'dup': 0, + 'out_of_order': 0, + 'seq_too_high': 0, + 'seq_too_low': 0}, + 'latency': {'average': 26.5, + 'hdrh': u'HISTFAAAAEx4nJNpmSgz...bFRgxi', + 'histogram': {20: 303, + 30: 320, + 40: 300, + 50: 73, + 60: 4, + 70: 1}, + 'jitter': 14, + 'last_max': 63, + 'total_max': 63, + 'total_min': 20}}, + 257: {'err_cntrs': {'dropped': 0, + 'dup': 0, + 'out_of_order': 0, + 'seq_too_high': 0, + 'seq_too_low': 0}, + 'latency': {'average': 29.75, + 'hdrh': u'HISTFAAAAEV4nJN...CALilDG0=', + 'histogram': {20: 261, + 30: 431, + 40: 3, + 50: 80, + 60: 225}, + 'jitter': 23, + 'last_max': 67, + 'total_max': 67, + 'total_min': 20}}, + 384: {'err_cntrs': {'dropped': 0, + 'dup': 0, + 'out_of_order': 0, + 'seq_too_high': 0, + 'seq_too_low': 0}, + 'latency': {'average': 18.0, + 'hdrh': u'HISTFAAAADR4nJNpm...MjCwDDxAZG', + 'histogram': {20: 987, 30: 14}, + 'jitter': 0, + 'last_max': 34, + 'total_max': 34, + 'total_min': 20}}, + 385: {'err_cntrs': {'dropped': 0, + 'dup': 0, + 'out_of_order': 0, + 'seq_too_high': 0, + 'seq_too_low': 0}, + 'latency': {'average': 19.0, + 'hdrh': u'HISTFAAAADR4nJNpm...NkYmJgDdagfK', + 'histogram': {20: 989, 30: 11}, + 'jitter': 0, + 'last_max': 38, + 'total_max': 38, + 'total_min': 20}}, + 'global': {'bad_hdr': 0, 'old_flow': 0}}, + """ total_max = 0 average = 0 total_min = float("inf") @@ -300,6 +366,17 @@ class TRex(AbstractTrafficGenerator): op="random") vm_param = [vxlan_udp_src_fv, STLVmWrFlowVar(fv_name="vxlan_udp_src", pkt_offset="UDP.sport")] + elif stream_cfg['mpls'] is True: + encap_level = '0' + pkt_base = Ether(src=stream_cfg['vtep_src_mac'], dst=stream_cfg['vtep_dst_mac']) + if stream_cfg['vtep_vlan'] is not None: + pkt_base /= Dot1Q(vlan=stream_cfg['vtep_vlan']) + if stream_cfg['mpls_outer_label'] is not None: + pkt_base /= MPLS(label=stream_cfg['mpls_outer_label'], cos=1, s=0, ttl=255) + if stream_cfg['mpls_inner_label'] is not None: + pkt_base /= MPLS(label=stream_cfg['mpls_inner_label'], cos=1, s=1, ttl=255) + # Flow stats and MPLS labels randomization TBD + pkt_base /= Ether(src=stream_cfg['mac_src'], dst=stream_cfg['mac_dst']) else: encap_level = '0' pkt_base = Ether(src=stream_cfg['mac_src'], dst=stream_cfg['mac_dst']) @@ -359,7 +436,8 @@ class TRex(AbstractTrafficGenerator): l4_type=CTRexVmInsFixHwCs.L4_TYPE_UDP)) pad = max(0, frame_size - len(pkt_base)) * 'x' - return STLPktBuilder(pkt=pkt_base / pad, vm=STLScVmRaw(vm_param)) + return STLPktBuilder(pkt=pkt_base / pad, + vm=STLScVmRaw(vm_param, cache_size=int(self.config.cache_size))) def generate_streams(self, port, chain_id, stream_cfg, l2frame, latency=True, e2e=False): @@ -374,21 +452,25 @@ class TRex(AbstractTrafficGenerator): """ streams = [] pg_id, lat_pg_id = self.get_pg_id(port, chain_id) + if self.config.no_flow_stats: + LOG.info("Traffic flow statistics are disabled.") if l2frame == 'IMIX': for ratio, l2_frame_size in zip(IMIX_RATIOS, IMIX_L2_SIZES): pkt = self._create_pkt(stream_cfg, l2_frame_size) - if e2e: + if e2e or stream_cfg['mpls']: streams.append(STLStream(packet=pkt, mode=STLTXCont(pps=ratio))) else: if stream_cfg['vxlan'] is True: streams.append(STLStream(packet=pkt, flow_stats=STLFlowStats(pg_id=pg_id, - vxlan=True), + vxlan=True) + if not self.config.no_flow_stats else None, mode=STLTXCont(pps=ratio))) else: streams.append(STLStream(packet=pkt, - flow_stats=STLFlowStats(pg_id=pg_id), + flow_stats=STLFlowStats(pg_id=pg_id) + if not self.config.no_flow_stats else None, mode=STLTXCont(pps=ratio))) if latency: @@ -398,18 +480,22 @@ class TRex(AbstractTrafficGenerator): else: l2frame_size = int(l2frame) pkt = self._create_pkt(stream_cfg, l2frame_size) - if e2e: + if e2e or stream_cfg['mpls']: streams.append(STLStream(packet=pkt, + # Flow stats is disabled for MPLS now + # flow_stats=STLFlowStats(pg_id=pg_id), mode=STLTXCont())) else: if stream_cfg['vxlan'] is True: streams.append(STLStream(packet=pkt, flow_stats=STLFlowStats(pg_id=pg_id, - vxlan=True), + vxlan=True) + if not self.config.no_flow_stats else None, mode=STLTXCont())) else: streams.append(STLStream(packet=pkt, - flow_stats=STLFlowStats(pg_id=pg_id), + flow_stats=STLFlowStats(pg_id=pg_id) + if not self.config.no_flow_stats else None, mode=STLTXCont())) # for the latency stream, the minimum payload is 16 bytes even in case of vlan tagging # without vlan, the min l2 frame size is 64 @@ -419,10 +505,19 @@ class TRex(AbstractTrafficGenerator): pkt = self._create_pkt(stream_cfg, 68) if latency: - # TRex limitation: VXLAN skip is not supported for latency stream - streams.append(STLStream(packet=pkt, - flow_stats=STLFlowLatencyStats(pg_id=lat_pg_id), - mode=STLTXCont(pps=self.LATENCY_PPS))) + if self.config.no_latency_stats: + LOG.info("Latency flow statistics are disabled.") + if stream_cfg['vxlan'] is True: + streams.append(STLStream(packet=pkt, + flow_stats=STLFlowLatencyStats(pg_id=lat_pg_id, + vxlan=True) + if not self.config.no_latency_stats else None, + mode=STLTXCont(pps=self.LATENCY_PPS))) + else: + streams.append(STLStream(packet=pkt, + flow_stats=STLFlowLatencyStats(pg_id=lat_pg_id) + if not self.config.no_latency_stats else None, + mode=STLTXCont(pps=self.LATENCY_PPS))) return streams @timeout(5) @@ -432,7 +527,7 @@ class TRex(AbstractTrafficGenerator): def __connect_after_start(self): # after start, Trex may take a bit of time to initialize # so we need to retry a few times - for it in xrange(self.config.generic_retry_count): + for it in range(self.config.generic_retry_count): try: time.sleep(1) self.client.connect() @@ -440,7 +535,7 @@ class TRex(AbstractTrafficGenerator): except Exception as ex: if it == (self.config.generic_retry_count - 1): raise - LOG.info("Retrying connection to TRex (%s)...", ex.message) + LOG.info("Retrying connection to TRex (%s)...", ex.msg) def connect(self): """Connect to the TRex server.""" @@ -505,7 +600,7 @@ class TRex(AbstractTrafficGenerator): if os.path.isfile(logpath): # Wait for TRex to finish writing error message last_size = 0 - for _ in xrange(self.config.generic_retry_count): + for _ in range(self.config.generic_retry_count): size = os.path.getsize(logpath) if size == last_size: # probably not writing anymore @@ -530,7 +625,7 @@ class TRex(AbstractTrafficGenerator): LOG.info("Restarting TRex ...") self.__stop_server() # Wait for server stopped - for _ in xrange(self.config.generic_retry_count): + for _ in range(self.config.generic_retry_count): time.sleep(1) if not self.client.is_connected(): LOG.info("TRex is stopped...") @@ -546,7 +641,7 @@ class TRex(AbstractTrafficGenerator): self.client.release(ports=ports) self.client.server_shutdown() except STLError as e: - LOG.warn('Unable to stop TRex. Error: %s', e) + LOG.warning('Unable to stop TRex. Error: %s', e) else: LOG.info('Using remote TRex. Unable to stop TRex') @@ -569,7 +664,7 @@ class TRex(AbstractTrafficGenerator): dst_macs = [None] * chain_count dst_macs_count = 0 # the index in the list is the chain id - if self.config.vxlan: + if self.config.vxlan or self.config.mpls: arps = [ ServiceARP(ctx, src_ip=device.vtep_src_ip, @@ -611,12 +706,12 @@ class TRex(AbstractTrafficGenerator): arp_dest_macs[port] = dst_macs LOG.info('ARP resolved successfully for port %s', port) break - else: - retry = attempt + 1 - LOG.info('Retrying ARP for: %s (retry %d/%d)', - unresolved, retry, self.config.generic_retry_count) - if retry < self.config.generic_retry_count: - time.sleep(self.config.generic_poll_sec) + + retry = attempt + 1 + LOG.info('Retrying ARP for: %s (retry %d/%d)', + unresolved, retry, self.config.generic_retry_count) + if retry < self.config.generic_retry_count: + time.sleep(self.config.generic_poll_sec) else: LOG.error('ARP timed out for port %s (resolved %d out of %d)', port, @@ -624,7 +719,10 @@ class TRex(AbstractTrafficGenerator): chain_count) break - self.client.set_service_mode(ports=self.port_handle, enabled=False) + # if the capture from the TRex console was started before the arp request step, + # it keeps 'service_mode' enabled, otherwise, it disables the 'service_mode' + if not self.config.service_mode: + self.client.set_service_mode(ports=self.port_handle, enabled=False) if len(arp_dest_macs) == len(self.port_handle): return arp_dest_macs return None @@ -766,7 +864,10 @@ class TRex(AbstractTrafficGenerator): 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) + # if the capture from TRex console was started before the connectivity step, + # it keeps 'service_mode' enabled, otherwise, it disables the 'service_mode' + if not self.config.service_mode: + self.client.set_service_mode(ports=self.port_handle, enabled=False) def cleanup(self): """Cleanup Trex driver.""" @@ -777,3 +878,7 @@ class TRex(AbstractTrafficGenerator): except STLError: # TRex does not like a reset while in disconnected state pass + + def set_service_mode(self, enabled=True): + """Enable/disable the 'service_mode'.""" + self.client.set_service_mode(ports=self.port_handle, enabled=enabled)