diff options
author | Yaroslav Brustinov <ybrustin@cisco.com> | 2016-04-11 01:47:32 +0300 |
---|---|---|
committer | Yaroslav Brustinov <ybrustin@cisco.com> | 2016-04-11 01:47:32 +0300 |
commit | af49c70aaaecb897c7d3b4bddb18f504d32b0546 (patch) | |
tree | 4b30e6a2533379fd3febd512aab4e6bc359c7ba7 /scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_hltapi.py | |
parent | 9b790cd6df9545ad69515560f6af62a6638a4093 (diff) |
hltapi stream stats, port tx/rx graphs at console
Diffstat (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_hltapi.py')
-rwxr-xr-x | scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_hltapi.py | 202 |
1 files changed, 122 insertions, 80 deletions
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_hltapi.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_hltapi.py index b506137b..1bec3b6b 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_hltapi.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_hltapi.py @@ -172,12 +172,22 @@ import sys import os import socket import copy -from collections import defaultdict +from collections import defaultdict, OrderedDict from .api import * from .trex_stl_types import * from .utils.common import get_number +class LRU_cache(OrderedDict): + def __init__(self, maxlen = 20, *args, **kwargs): + OrderedDict.__init__(self, *args, **kwargs) + self.maxlen = maxlen + + def __setitem__(self, *args, **kwargs): + OrderedDict.__setitem__(self, *args, **kwargs) + if len(self) > self.maxlen: + self.popitem(last = False) + class HLT_ERR(dict): def __init__(self, log = 'Unknown error', **kwargs): @@ -201,7 +211,7 @@ def merge_kwargs(default_kwargs, user_kwargs): for key, value in user_kwargs.items(): if key in kwargs: kwargs[key] = value - elif key in ('save_to_yaml', 'save_to_pcap'): # internal debug arguments + elif key in ('save_to_yaml', 'save_to_pcap', 'pg_id'): # internal arguments kwargs[key] = value else: print("Warning: provided parameter '%s' is not supported" % key) @@ -338,8 +348,11 @@ class CTRexHltApi(object): def __init__(self, verbose = 0): self.trex_client = None self.verbose = verbose - self._streams_history = {} # streams per stream_id per port in format of HLT arguments for modify later - + self._last_pg_id = 0 # pg_id acts as stream_handle + self._streams_history = {} # streams in format of HLT arguments for modify later + self._native_handle_by_pg_id = {} # pg_id -> native handle + port + self._pg_id_by_id = {} # stream_id -> pg_id + self._pg_id_by_name = {} # name -> pg_id ########################### # Session functions # @@ -375,9 +388,6 @@ class CTRexHltApi(object): self.trex_client = None return HLT_ERR('Could not acquire ports %s: %s' % (port_list, e if isinstance(e, STLError) else traceback.format_exc())) - # since only supporting single TRex at the moment, 1:1 map - port_handle = self.trex_client.get_acquired_ports() - # arrived here, all desired ports were successfully acquired if kwargs['reset']: # remove all port traffic configuration from TRex @@ -389,7 +399,7 @@ class CTRexHltApi(object): return HLT_ERR('Error in reset traffic: %s' % e if isinstance(e, STLError) else traceback.format_exc()) self._streams_history = CStreamsPerPort(hlt_history = True) - return HLT_OK(port_handle = port_handle) + return HLT_OK(port_handle = dict([(port_id, port_id) for port_id in port_list])) def cleanup_session(self, **user_kwargs): kwargs = merge_kwargs(cleanup_session_kwargs, user_kwargs) @@ -446,23 +456,9 @@ class CTRexHltApi(object): kwargs = merge_kwargs(traffic_config_kwargs, user_kwargs) stream_id = kwargs['stream_id'] mode = kwargs['mode'] - if type(stream_id) is list: - if len(stream_id) > 1: - streams_per_port = CStreamsPerPort() - for each_stream_id in stream_id: - user_kwargs[stream_id] = each_stream_id - res = self.traffic_config(**user_kwargs) - if type(res) is HLT_ERR: - return res - streams_per_port.add_streams_from_res(res) - if mode == 'create': - return HLT_OK(stream_id = streams_per_port) - else: - return HLT_OK() - else: - stream_id = stream_id[0] - + pg_id = None port_handle = port_list = self._parse_port_list(kwargs['port_handle']) + ALLOWED_MODES = ['create', 'modify', 'remove', 'enable', 'disable', 'reset'] if mode not in ALLOWED_MODES: return HLT_ERR('Mode must be one of the following values: %s' % ALLOWED_MODES) @@ -480,7 +476,7 @@ class CTRexHltApi(object): if mode == 'remove': if stream_id is None: return HLT_ERR('Please specify stream_id to remove.') - if type(stream_id) is str and stream_id == 'all': + if stream_id == 'all': try: self.trex_client.remove_all_streams(port_handle) for port in port_handle: @@ -504,14 +500,14 @@ class CTRexHltApi(object): # self._streams_history[stream_id].update(kwargs) # <- the modification if mode == 'modify': # we remove stream and create new one with same stream_id - stream_id = kwargs.get('stream_id') - if stream_id is None: + pg_id = kwargs.get('stream_id') + if pg_id is None: return HLT_ERR('Please specify stream_id to modify.') if len(port_handle) > 1: for port in port_handle: - user_kwargs[port_handle] = port - res = self.traffic_config(**user_kwargs) # recurse per port, each port can have different stream with such id + user_kwargs['port_handle'] = port + res = self.traffic_config(**user_kwargs) else: if type(port_handle) is list: port = port_handle[0] @@ -519,9 +515,9 @@ class CTRexHltApi(object): port = port_handle if port not in self._streams_history: return HLT_ERR('Port %s was not used/acquired' % port) - if stream_id not in self._streams_history[port]: + if pg_id not in self._streams_history[port]: return HLT_ERR('This stream_id (%s) was not used before at port %s, please create new.' % (stream_id, port)) - kwargs.update(self._streams_history[port][stream_id]) + kwargs.update(self._streams_history[port][pg_id]) kwargs.update(user_kwargs) try: self.trex_client.remove_streams(stream_id, port_handle) @@ -530,24 +526,24 @@ class CTRexHltApi(object): if mode == 'create' or mode == 'modify': # create a new stream with desired attributes, starting by creating packet - streams_per_port = CStreamsPerPort() if is_true(kwargs['bidirectional']): # two streams with opposite directions del user_kwargs['bidirectional'] + stream_per_port = {} save_to_yaml = user_kwargs.get('save_to_yaml') bidirect_err = 'When using bidirectional flag, ' if len(port_handle) != 1: return HLT_ERR(bidirect_err + 'port_handle1 should be single port handle.') + port_handle = port_handle[0] port_handle2 = kwargs['port_handle2'] if (type(port_handle2) is list and len(port_handle2) > 1) or port_handle2 is None: return HLT_ERR(bidirect_err + 'port_handle2 should be single port handle.') try: if save_to_yaml and type(save_to_yaml) is str: user_kwargs['save_to_yaml'] = save_to_yaml.replace('.yaml', '_bi1.yaml') - user_kwargs['port_handle'] = port_handle[0] res1 = self.traffic_config(**user_kwargs) if res1['status'] == 0: raise STLError('Could not create bidirectional stream 1: %s' % res1['log']) - streams_per_port.add_streams_from_res(res1) + stream_per_port[port_handle] = res1['stream_id'] kwargs['direction'] = 1 - kwargs['direction'] # not correct_direction(user_kwargs, kwargs) if save_to_yaml and type(save_to_yaml) is str: @@ -556,11 +552,11 @@ class CTRexHltApi(object): res2 = self.traffic_config(**user_kwargs) if res2['status'] == 0: raise STLError('Could not create bidirectional stream 2: %s' % res2['log']) - streams_per_port.add_streams_from_res(res2) + stream_per_port[port_handle2] = res2['stream_id'] except Exception as e: return HLT_ERR('Could not generate bidirectional traffic: %s' % e if isinstance(e, STLError) else traceback.format_exc()) if mode == 'create': - return HLT_OK(stream_id = streams_per_port) + return HLT_OK(stream_id = stream_per_port) else: return HLT_OK() @@ -568,6 +564,9 @@ class CTRexHltApi(object): if kwargs['load_profile']: stream_obj = STLProfile.load_py(kwargs['load_profile'], direction = kwargs['direction']) else: + if not pg_id: + pg_id = self._get_available_pg_id() + user_kwargs['pg_id'] = pg_id stream_obj = STLHltStream(**user_kwargs) except Exception as e: return HLT_ERR('Could not create stream: %s' % e if isinstance(e, STLError) else traceback.format_exc()) @@ -583,10 +582,7 @@ class CTRexHltApi(object): except Exception as e: return HLT_ERR('Could not add stream to ports: %s' % e if isinstance(e, STLError) else traceback.format_exc()) if mode == 'create': - if len(stream_id_arr) == 1: - return HLT_OK(stream_id = dict((port, stream_id_arr[0]) for port in port_handle)) - else: - return HLT_OK(stream_id = dict((port, stream_id_arr) for port in port_handle)) + return HLT_OK(stream_id = pg_id) else: return HLT_OK() @@ -652,42 +648,64 @@ class CTRexHltApi(object): kwargs = merge_kwargs(traffic_stats_kwargs, user_kwargs) mode = kwargs['mode'] port_handle = kwargs['port_handle'] + if type(port_handle) is not list: + port_handle = [port_handle] ALLOWED_MODES = ['aggregate', 'streams', 'all'] if mode not in ALLOWED_MODES: return HLT_ERR("'mode' must be one of the following values: %s" % ALLOWED_MODES) - if mode == 'streams': - return HLT_ERR("mode 'streams' not implemented'") - if mode in ('all', 'aggregate'): - hlt_stats_dict = {} - try: - stats = self.trex_client.get_stats(port_handle) - except Exception as e: - return HLT_ERR('Could not retrieve stats: %s' % e if isinstance(e, STLError) else traceback.format_exc()) - for port_id, stat_dict in stats.items(): - if is_integer(port_id): - hlt_stats_dict[port_id] = { - 'aggregate': { - 'tx': { - 'pkt_bit_rate': stat_dict.get('tx_bps'), - 'pkt_byte_count': stat_dict.get('obytes'), - 'pkt_count': stat_dict.get('opackets'), - 'pkt_rate': stat_dict.get('tx_pps'), - 'total_pkt_bytes': stat_dict.get('obytes'), - 'total_pkt_rate': stat_dict.get('tx_pps'), - 'total_pkts': stat_dict.get('opackets'), - }, - 'rx': { - 'pkt_bit_rate': stat_dict.get('rx_bps'), - 'pkt_byte_count': stat_dict.get('ibytes'), - 'pkt_count': stat_dict.get('ipackets'), - 'pkt_rate': stat_dict.get('rx_pps'), - 'total_pkt_bytes': stat_dict.get('ibytes'), - 'total_pkt_rate': stat_dict.get('rx_pps'), - 'total_pkts': stat_dict.get('ipackets'), + hlt_stats_dict = dict([(port, {}) for port in port_handle]) + try: + stats = self.trex_client.get_stats(port_handle) + if mode in ('all', 'aggregate'): + for port_id in port_handle: + port_stats = stats[port_id] + if is_integer(port_id): + hlt_stats_dict[port_id]['aggregate'] = { + 'tx': { + 'pkt_bit_rate': port_stats.get('tx_bps', 0), + 'pkt_byte_count': port_stats.get('obytes', 0), + 'pkt_count': port_stats.get('opackets', 0), + 'pkt_rate': port_stats.get('tx_pps', 0), + 'total_pkt_bytes': port_stats.get('obytes', 0), + 'total_pkt_rate': port_stats.get('tx_pps', 0), + 'total_pkts': port_stats.get('opackets', 0), + }, + 'rx': { + 'pkt_bit_rate': port_stats.get('rx_bps', 0), + 'pkt_byte_count': port_stats.get('ibytes', 0), + 'pkt_count': port_stats.get('ipackets', 0), + 'pkt_rate': port_stats.get('rx_pps', 0), + 'total_pkt_bytes': port_stats.get('ibytes', 0), + 'total_pkt_rate': port_stats.get('rx_pps', 0), + 'total_pkts': port_stats.get('ipackets', 0), + } + } + if mode in ('all', 'streams'): + for pg_id, pg_stats in stats['flow_stats'].items(): + for port_id in port_handle: + if 'stream' not in hlt_stats_dict[port_id]: + hlt_stats_dict[port_id]['stream'] = {} + hlt_stats_dict[port_id]['stream'][pg_id] = { + 'tx': { + 'total_pkts': pg_stats['tx_pkts'].get(port_id, 0), + 'total_pkt_bytes': pg_stats['tx_bytes'].get(port_id, 0), + 'total_pkts_bytes': pg_stats['tx_bytes'].get(port_id, 0), + 'total_pkt_bit_rate': pg_stats['tx_bps'].get(port_id, 0), + 'total_pkt_rate': pg_stats['tx_pps'].get(port_id, 0), + 'line_rate_percentage': pg_stats['tx_line_util'].get(port_id, 0), + }, + 'rx': { + 'total_pkts': pg_stats['rx_pkts'].get(port_id, 0), + 'total_pkt_bytes': pg_stats['rx_bytes'].get(port_id, 0), + 'total_pkts_bytes': pg_stats['rx_bytes'].get(port_id, 0), + 'total_pkt_bit_rate': pg_stats['rx_bps'].get(port_id, 0), + 'total_pkt_rate': pg_stats['rx_pps'].get(port_id, 0), + 'line_rate_percentage': pg_stats['rx_line_util'].get(port_id, 0), + }, } - } - } - return HLT_OK(hlt_stats_dict) + except Exception as e: + return HLT_ERR('Could not retrieve stats: %s' % e if isinstance(e, STLError) else traceback.format_exc()) + return HLT_OK(hlt_stats_dict) # timeout = maximal time to wait def wait_on_traffic(self, port_handle = None, timeout = 60): @@ -700,6 +718,18 @@ class CTRexHltApi(object): # Private functions # ########################### + def _get_available_pg_id(self): + pg_id = self._last_pg_id + used_pg_ids = self.trex_client.get_stats()['flow_stats'].keys() + for i in range(65535): + pg_id += 1 + if pg_id not in used_pg_ids: + self._last_pg_id = pg_id + return pg_id + if pg_id == 65535: + pg_id = 0 + raise STLError('Could not find free pg_id in range [1, 65535].') + # remove streams from given port(s). # stream_id can be: # * int - exact stream_id value @@ -812,30 +842,33 @@ def STLHltStream(**user_kwargs): # packet generation packet = generate_packet(**user_kwargs) + + # stream generation try: rate_types_dict = {'rate_pps': 'pps', 'rate_bps': 'bps_L2', 'rate_percent': 'percentage'} rate_stateless = {rate_types_dict[rate_key]: float(kwargs[rate_key])} transmit_mode = kwargs['transmit_mode'] pkts_per_burst = kwargs['pkts_per_burst'] if transmit_mode == 'continuous': - transmit_mode_class = STLTXCont(**rate_stateless) + transmit_mode_obj = STLTXCont(**rate_stateless) elif transmit_mode == 'single_burst': - transmit_mode_class = STLTXSingleBurst(total_pkts = pkts_per_burst, **rate_stateless) + transmit_mode_obj = STLTXSingleBurst(total_pkts = pkts_per_burst, **rate_stateless) elif transmit_mode == 'multi_burst': - transmit_mode_class = STLTXMultiBurst(total_pkts = pkts_per_burst, count = int(kwargs['burst_loop_count']), + transmit_mode_obj = STLTXMultiBurst(total_pkts = pkts_per_burst, count = int(kwargs['burst_loop_count']), ibg = kwargs['inter_burst_gap'], **rate_stateless) else: raise STLError('transmit_mode %s not supported/implemented') except Exception as e: - raise STLError('Could not create transmit_mode class %s: %s' % (transmit_mode, e if isinstance(e, STLError) else traceback.format_exc())) + raise STLError('Could not create transmit_mode object %s: %s' % (transmit_mode, e if isinstance(e, STLError) else traceback.format_exc())) - # stream generation try: + pg_id = kwargs.get('pg_id') stream = STLStream(packet = packet, random_seed = 1 if is_true(kwargs['consistent_random']) else 0, #enabled = True, #self_start = True, - mode = transmit_mode_class, + flow_stats = STLFlowStats(pg_id) if pg_id else None, + mode = transmit_mode_obj, stream_id = kwargs['stream_id'], name = kwargs['name'], ) @@ -848,8 +881,12 @@ def STLHltStream(**user_kwargs): stream.dump_to_yaml(debug_filename) return stream +packet_cache = LRU_cache(maxlen = 20) + def generate_packet(**user_kwargs): correct_macs(user_kwargs) + if repr(user_kwargs) in packet_cache: + return packet_cache[repr(user_kwargs)] kwargs = merge_kwargs(traffic_config_kwargs, user_kwargs) correct_sizes(kwargs) # we are producing the packet - 4 bytes fcs correct_direction(kwargs, kwargs) @@ -868,8 +905,12 @@ def generate_packet(**user_kwargs): kwargs['mac_dst'] = None kwargs['mac_src_mode'] = 'fixed' kwargs['mac_dst_mode'] = 'fixed' - - l2_layer = Ether(src = kwargs['mac_src'], dst = kwargs['mac_dst']) + ethernet_kwargs = {} + if kwargs['mac_src']: + ethernet_kwargs['src'] = kwargs['mac_src'] + if kwargs['mac_dst']: + ethernet_kwargs['dst'] = kwargs['mac_dst'] + l2_layer = Ether(**ethernet_kwargs) # Eth VM, change only 32 lsb if kwargs['mac_src_mode'] != 'fixed': @@ -1475,6 +1516,7 @@ def generate_packet(**user_kwargs): debug_filename = kwargs.get('save_to_pcap') if type(debug_filename) is str: pkt.dump_pkt_to_pcap(debug_filename) + packet_cache[repr(user_kwargs)] = pkt return pkt def get_TOS(user_kwargs, kwargs): |