diff options
author | 2016-02-24 14:55:59 +0200 | |
---|---|---|
committer | 2016-02-24 14:55:59 +0200 | |
commit | 5a844c9d72411435842e5a0674c6fdc04e5d4e84 (patch) | |
tree | dc2738fe0638ac0ed856c70564bc7096bb33868e /scripts/automation/trex_control_plane/stl/trex_stl_lib | |
parent | 9e616d8eecaefbf538174328edfce8fd96336ce6 (diff) | |
parent | 3eb4f868ef5bc728a46c8012c914f6a9381f4cdb (diff) |
Merge GARP example
Diffstat (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib')
6 files changed, 181 insertions, 94 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 581ea418..593c4c47 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 @@ -27,9 +27,9 @@ traffic_config_kwargs = { 'port_handle2': None, # stream builder parameters 'transmit_mode': 'continuous', # ( continuous | multi_burst | single_burst ) - 'rate_pps': 1, # TODO: support bps and percent once stateless API will, use rate_percent by default + 'rate_pps': None, 'rate_bps': None, - 'rate_percent': 100, + 'rate_percent': 10, 'stream_id': None, 'name': None, 'bidirectional': 0, @@ -663,55 +663,67 @@ class CTRexHltApi(object): def STLHltStream(**user_kwargs): kwargs = merge_kwargs(traffic_config_kwargs, user_kwargs) + # verify rate is given by at most one arg + rate_args = set(['rate_pps', 'rate_bps', 'rate_percent']) + intersect_rate_args = list(rate_args & set(user_kwargs.keys())) + if len(intersect_rate_args) > 1: + raise STLError('More than one rate argument specified: %s' % intersect_rate_args) + try: + rate_key = intersect_rate_args[0] + except IndexError: + rate_key = 'rate_percent' + if kwargs['length_mode'] == 'imix': # several streams with given length streams_arr = [] user_kwargs['length_mode'] = 'fixed' if kwargs['l3_imix1_size'] < 32 or kwargs['l3_imix2_size'] < 32 or kwargs['l3_imix3_size'] < 32 or kwargs['l3_imix4_size'] < 32: raise STLError('l3_imix*_size should be at least 32') - total_rate = kwargs['l3_imix1_ratio'] + kwargs['l3_imix2_ratio'] + kwargs['l3_imix3_ratio'] + kwargs['l3_imix4_ratio'] - if total_rate == 0: + total_ratio = kwargs['l3_imix1_ratio'] + kwargs['l3_imix2_ratio'] + kwargs['l3_imix3_ratio'] + kwargs['l3_imix4_ratio'] + if total_ratio == 0: raise STLError('Used length_mode imix, but all the ratios are 0') save_to_yaml = kwargs.get('save_to_yaml') - rate_pps = float(kwargs['rate_pps']) + total_rate = float(kwargs[rate_key]) if kwargs['l3_imix1_ratio'] > 0: if save_to_yaml and type(save_to_yaml) is str: user_kwargs['save_to_yaml'] = save_to_yaml.replace('.yaml', '_imix1.yaml') user_kwargs['frame_size'] = kwargs['l3_imix1_size'] - user_kwargs['rate_pps'] = rate_pps * kwargs['l3_imix1_ratio'] / total_rate + user_kwargs[rate_key] = total_rate * kwargs['l3_imix1_ratio'] / total_ratio streams_arr.append(STLHltStream(**user_kwargs)) if kwargs['l3_imix2_ratio'] > 0: if save_to_yaml and type(save_to_yaml) is str: user_kwargs['save_to_yaml'] = save_to_yaml.replace('.yaml', '_imix2.yaml') user_kwargs['frame_size'] = kwargs['l3_imix2_size'] - user_kwargs['rate_pps'] = rate_pps * kwargs['l3_imix2_ratio'] / total_rate + user_kwargs[rate_key] = total_rate * kwargs['l3_imix2_ratio'] / total_ratio streams_arr.append(STLHltStream(**user_kwargs)) if kwargs['l3_imix3_ratio'] > 0: if save_to_yaml and type(save_to_yaml) is str: user_kwargs['save_to_yaml'] = save_to_yaml.replace('.yaml', '_imix3.yaml') user_kwargs['frame_size'] = kwargs['l3_imix3_size'] - user_kwargs['rate_pps'] = rate_pps * kwargs['l3_imix3_ratio'] / total_rate + user_kwargs[rate_key] = total_rate * kwargs['l3_imix3_ratio'] / total_ratio streams_arr.append(STLHltStream(**user_kwargs)) if kwargs['l3_imix4_ratio'] > 0: if save_to_yaml and type(save_to_yaml) is str: user_kwargs['save_to_yaml'] = save_to_yaml.replace('.yaml', '_imix4.yaml') user_kwargs['frame_size'] = kwargs['l3_imix4_size'] - user_kwargs['rate_pps'] = rate_pps * kwargs['l3_imix4_ratio'] / total_rate + user_kwargs[rate_key] = total_rate * kwargs['l3_imix4_ratio'] / total_ratio streams_arr.append(STLHltStream(**user_kwargs)) return streams_arr # packet generation packet = generate_packet(**user_kwargs) try: + # TODO: verify if bps is L1 or L2, use L2 for now + 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'] - rate_pps = kwargs['rate_pps'] pkts_per_burst = kwargs['pkts_per_burst'] if transmit_mode == 'continuous': - transmit_mode_class = STLTXCont(pps = rate_pps) + transmit_mode_class = STLTXCont(**rate_stateless) elif transmit_mode == 'single_burst': - transmit_mode_class = STLTXSingleBurst(pps = rate_pps, total_pkts = pkts_per_burst) + transmit_mode_class = STLTXSingleBurst(total_pkts = pkts_per_burst, **rate_stateless) elif transmit_mode == 'multi_burst': - transmit_mode_class = STLTXMultiBurst(pps = rate_pps, total_pkts = pkts_per_burst, - count = int(kwargs['burst_loop_count']), ibg = kwargs['inter_burst_gap']) + transmit_mode_class = 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: diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py index 29bad041..4dd07a13 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py @@ -1,6 +1,9 @@ from collections import namedtuple, OrderedDict +from trex_stl_packet_builder_scapy import CScapyTRexPktBuilder +from trex_stl_streams import STLStream +import base64 import trex_stl_stats from trex_stl_types import * import time @@ -132,10 +135,23 @@ class Port(object): else: raise Exception("port {0}: bad state received from server '{1}'".format(self.port_id, port_state)) - # TODO: handle syncing the streams into stream_db self.next_available_id = long(rc.data()['max_stream_id']) + 1 + # sync the streams + params = {"port_id": self.port_id} + + command = RpcCmdData("get_all_streams", params) + rc = self.transmit(command.method, command.params) + if rc.bad(): + return self.err(rc.err()) + + for k, v in rc.data()['streams'].iteritems(): + self.streams[k] = {'next_id': v['next_stream_id'], + 'pkt' : base64.b64decode(v['packet']['binary']), + 'mode' : v['mode']['type'], + 'rate' : STLStream.get_rate_from_field(v['mode']['rate'])} + return self.ok() @@ -161,55 +177,64 @@ class Port(object): return self.err("Please stop port before attempting to add streams") # listify - streams_list = copy.deepcopy(streams_list if isinstance(streams_list, list) else [streams_list]) + streams_list = streams_list if isinstance(streams_list, list) else [streams_list] lookup = {} # allocate IDs for stream in streams_list: - if stream.get_id() == None: - stream.set_id(self.__allocate_stream_id()) - lookup[stream.get_name()] = stream.get_id() + # allocate stream id + stream_id = stream.get_id() if stream.get_id() is not None else self.__allocate_stream_id() + if stream_id in self.streams: + return self.err('Stream ID: {0} already exists'.format(stream_id)) - batch = [] + # name + name = stream.get_name() if stream.get_name() is not None else id(stream) + if name in lookup: + return self.err("multiple streams with duplicate name: '{0}'".format(name)) + lookup[name] = stream_id - + batch = [] for stream in streams_list: + name = stream.get_name() if stream.get_name() is not None else id(stream) + stream_id = lookup[name] next_id = -1 + next = stream.get_next() if next: if not next in lookup: return self.err("stream dependency error - unable to find '{0}'".format(next)) next_id = lookup[next] - - stream.set_next_id(next_id) - - stream_json = stream.to_json() - stream_json['next_stream_id'] = stream.get_next_id() + stream_json['next_stream_id'] = next_id params = {"handler": self.handler, "port_id": self.port_id, - "stream_id": stream.get_id(), + "stream_id": stream_id, "stream": stream_json} cmd = RpcCmdData('add_stream', params) batch.append(cmd) - self.streams[stream.get_id()] = stream rc = self.transmit_batch(batch) - if not rc: - return self.err(str(rc)) + for i, single_rc in enumerate(rc): + if single_rc: + stream_id = batch[i].params['stream_id'] + next_id = batch[i].params['stream']['next_stream_id'] + self.streams[stream_id] = {'next_id' : next_id, + 'pkt' : streams_list[i].get_pkt(), + 'mode' : streams_list[i].get_mode(), + 'rate' : streams_list[i].get_rate()} - # the only valid state now - self.state = self.STATE_STREAMS - return self.ok() + self.state = self.STATE_STREAMS if (len(self.streams) > 0) else self.STATE_IDLE + + return self.ok() if rc else self.err(str(rc)) @@ -239,16 +264,16 @@ class Port(object): cmd = RpcCmdData('remove_stream', params) batch.append(cmd) - del self.streams[stream_id] - rc = self.transmit_batch(batch) - if not rc: - return self.err(rc.err()) + for i, single_rc in enumerate(rc): + if single_rc: + id = batch[i].params['stream_id'] + del self.streams[stream_id] self.state = self.STATE_STREAMS if (len(self.streams) > 0) else self.STATE_IDLE - return self.ok() + return self.ok() if rc else self.err(rc.err()) # remove all the streams @@ -273,6 +298,7 @@ class Port(object): return self.ok() + # get a specific stream def get_stream (self, stream_id): if stream_id in self.streams: @@ -283,6 +309,7 @@ class Port(object): def get_all_streams (self): return self.streams + # start traffic def start (self, mul, duration, force): if not self.is_acquired(): @@ -324,7 +351,6 @@ class Port(object): return self.ok() - params = {"handler": self.handler, "port_id": self.port_id} @@ -421,6 +447,7 @@ class Port(object): return self.ok() + def get_profile (self): return self.profile @@ -496,18 +523,24 @@ class Port(object): return {} data = {} - for id, stream in self.streams.iteritems(): + for id, obj in self.streams.iteritems(): + + # lazy build scapy repr. + if not 'pkt_type' in obj: + obj['pkt_type'] = CScapyTRexPktBuilder.pkt_layers_desc_from_buffer(obj['pkt']) + data[id] = OrderedDict([ ('id', id), - ('packet_type', stream.get_pkt_type()), - ('L2 len', stream.get_pkt_len()), - ('mode' , stream.get_mode()), - ('rate_pps', stream.get_pps()), - ('next_stream', stream.get_next_id()) + ('packet_type', obj['pkt_type']), + ('L2 len', len(obj['pkt']) + 4), + ('mode', obj['mode']), + ('rate', obj['rate']), + ('next_stream', obj['next_id']) ]) return {"streams" : OrderedDict(sorted(data.items())) } + ################# events handler ###################### def async_event_port_stopped (self): self.state = self.STATE_STREAMS diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_sim.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_sim.py index 907125e9..54d699d8 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_sim.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_sim.py @@ -152,19 +152,29 @@ class STLSim(object): # load streams cmds_json = [] - id = 1 + id_counter = 1 lookup = {} + # allocate IDs for stream in stream_list: - if stream.get_id() == None: - stream.set_id(id) - id += 1 + if stream.get_id() is not None: + stream_id = stream.get_id() + else: + stream_id = id_counter + id_counter += 1 - lookup[stream.get_name()] = stream.get_id() + name = stream.get_name() if stream.get_name() is not None else id(stream) + if name in lookup: + raise STLError("multiple streams with name: '{0}'".format(name)) + lookup[name] = stream_id # resolve names for stream in stream_list: + + name = stream.get_name() if stream.get_name() is not None else id(stream) + stream_id = lookup[name] + next_id = -1 next = stream.get_next() if next: @@ -172,19 +182,16 @@ class STLSim(object): raise STLError("stream dependency error - unable to find '{0}'".format(next)) next_id = lookup[next] - stream.set_next_id(next_id) - - for stream in stream_list: stream_json = stream.to_json() - stream_json['next_stream_id'] = stream.get_next_id() + stream_json['next_stream_id'] = next_id cmd = {"id":1, "jsonrpc": "2.0", "method": "add_stream", "params": {"handler": self.handler, "port_id": self.port_id, - "stream_id": stream.get_id(), + "stream_id": stream_id, "stream": stream_json} } diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py index 34c7a857..ebc686f8 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py @@ -224,13 +224,12 @@ class CTRexInfoGenerator(object): p_type_field_len = 0 for stream_id, stream_id_sum in return_streams_data['streams'].iteritems(): - stream_id_sum['rate_pps'] = format_num(stream_id_sum['rate_pps'], suffix='pps') stream_id_sum['packet_type'] = self._trim_packet_headers(stream_id_sum['packet_type'], 30) p_type_field_len = max(p_type_field_len, len(stream_id_sum['packet_type'])) info_table = text_tables.TRexTextTable() info_table.set_cols_align(["c"] + ["l"] + ["r"] + ["c"] + ["r"] + ["c"]) - info_table.set_cols_width([10] + [p_type_field_len] + [8] + [16] + [10] + [12]) + info_table.set_cols_width([10] + [p_type_field_len] + [8] + [16] + [15] + [12]) info_table.set_cols_dtype(["t"] + ["t"] + ["t"] + ["t"] + ["t"] + ["t"]) info_table.add_rows([v.values() diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py index f79d25c3..127d1669 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py @@ -64,9 +64,9 @@ class STLTXMode(object): # continuous mode class STLTXCont(STLTXMode): - def __init__ (self, pps = None, bps_L1 = None, bps_L2 = None, percentage = None): + def __init__ (self, **kwargs): - super(STLTXCont, self).__init__(pps, bps_L1, bps_L2, percentage) + super(STLTXCont, self).__init__(**kwargs) self.fields['type'] = 'continuous' @@ -76,12 +76,12 @@ class STLTXCont(STLTXMode): # single burst mode class STLTXSingleBurst(STLTXMode): - def __init__ (self, total_pkts = 1, pps = None, bps_L1 = None, bps_L2 = None, percentage = None): + def __init__ (self, total_pkts = 1, **kwargs): if not isinstance(total_pkts, int): raise STLArgumentError('total_pkts', total_pkts) - super(STLTXSingleBurst, self).__init__(pps, bps_L1, bps_L2, percentage) + super(STLTXSingleBurst, self).__init__(**kwargs) self.fields['type'] = 'single_burst' self.fields['total_pkts'] = total_pkts @@ -96,10 +96,7 @@ class STLTXMultiBurst(STLTXMode): pkts_per_burst = 1, ibg = 0.0, # usec not SEC count = 1, - pps = None, - bps_L1 = None, - bps_L2 = None, - percentage = None): + **kwargs): if not isinstance(pkts_per_burst, int): raise STLArgumentError('pkts_per_burst', pkts_per_burst) @@ -110,7 +107,7 @@ class STLTXMultiBurst(STLTXMode): if not isinstance(count, int): raise STLArgumentError('count', count) - super(STLTXMultiBurst, self).__init__(pps, bps_L1, bps_L2, percentage) + super(STLTXMultiBurst, self).__init__(**kwargs) self.fields['type'] = 'multi_burst' self.fields['pkts_per_burst'] = pkts_per_burst @@ -124,8 +121,24 @@ STLStreamDstMAC_CFG_FILE=0 STLStreamDstMAC_PKT =1 STLStreamDstMAC_ARP =2 +# RX stats class +class STLRxStats(object): + def __init__ (self, user_id): + self.fields = {} + + self.fields['enabled'] = True + self.fields['stream_id'] = user_id + self.fields['seq_enabled'] = False + self.fields['latency_enabled'] = False + def to_json (self): + return dict(self.fields) + + @staticmethod + def defaults (): + return {'enabled' : False} + class STLStream(object): def __init__ (self, @@ -158,9 +171,7 @@ class STLStream(object): self.name = name self.next = next - # ID - self.set_id(stream_id) - self.set_next_id(None) + self.id = stream_id self.fields = {} @@ -221,10 +232,9 @@ class STLStream(object): self.packet_desc = None if not rx_stats: - self.fields['rx_stats'] = {} - self.fields['rx_stats']['enabled'] = False + self.fields['rx_stats'] = STLRxStats.defaults() else: - self.fields['rx_stats'] = rx_stats + self.fields['rx_stats'] = rx_stats.to_json() def __str__ (self): @@ -239,14 +249,6 @@ class STLStream(object): def get_id (self): return self.id - def set_id (self, id): - self.id = id - - def get_next_id (self): - return self.next_id - - def set_next_id (self, next_id): - self.next_id = next_id def get_name (self): return self.name @@ -254,26 +256,44 @@ class STLStream(object): def get_next (self): return self.next - def get_pkt_type (self): - if self.packet_desc == None: - self.packet_desc = CScapyTRexPktBuilder.pkt_layers_desc_from_buffer(self.get_pkt()) - - return self.packet_desc def get_pkt (self): return self.pkt def get_pkt_len (self, count_crc = True): - pkt_len = len(base64.b64decode(self.get_pkt())) + pkt_len = len(self.get_pkt()) if count_crc: pkt_len += 4 return pkt_len + def get_pkt_type (self): + if self.packet_desc == None: + self.packet_desc = CScapyTRexPktBuilder.pkt_layers_desc_from_buffer(self.get_pkt()) + + return self.packet_desc + def get_mode (self): return self.mode_desc + @staticmethod + def get_rate_from_field (rate_json): + t = rate_json['type'] + v = rate_json['value'] + + if t == "pps": + return format_num(v, suffix = "pps") + elif t == "bps_L1": + return format_num(v, suffix = "bps (L1)") + elif t == "bps_L2": + return format_num(v, suffix = "bps (L2)") + elif t == "percentage": + return format_num(v, suffix = "%") + + def get_rate (self): + return self.get_rate_from_field(self.fields['mode']['rate']) + def to_yaml (self): y = {} @@ -338,6 +358,8 @@ class YAMLLoader(object): def __parse_mode (self, mode_obj): + if not mode_obj: + return None rate_parser = set(mode_obj).intersection(['pps', 'bps_L1', 'bps_L2', 'percentage']) if len(rate_parser) != 1: @@ -371,6 +393,18 @@ class YAMLLoader(object): + def __parse_rx_stats (self, rx_stats_obj): + + # no such object + if not rx_stats_obj or rx_stats_obj.get('enabled') == False: + return None + + user_id = rx_stats_obj.get('stream_id') + if user_id == None: + raise STLError("enabled RX stats section must contain 'stream_id' field") + + return STLRxStats(user_id = user_id) + def __parse_stream (self, yaml_object): s_obj = yaml_object['stream'] @@ -384,23 +418,21 @@ class YAMLLoader(object): # mode - mode_obj = s_obj.get('mode') - if not mode_obj: - raise STLError("YAML file must contain 'mode' field") - - mode = self.__parse_mode(mode_obj) + mode = self.__parse_mode(s_obj.get('mode')) + # rx stats + rx_stats = self.__parse_rx_stats(s_obj.get('rx_stats')) - defaults = STLStream() + defaults = STLStream() # create the stream stream = STLStream(name = yaml_object.get('name'), packet = builder, mode = mode, + rx_stats = rx_stats, enabled = s_obj.get('enabled', defaults.fields['enabled']), self_start = s_obj.get('self_start', defaults.fields['self_start']), isg = s_obj.get('isg', defaults.fields['isg']), - rx_stats = s_obj.get('rx_stats', defaults.fields['rx_stats']), next = yaml_object.get('next'), action_count = s_obj.get('action_count', defaults.fields['action_count']), mac_src_override_by_pkt = s_obj.get('mac_src_override_by_pkt', 0), @@ -523,7 +555,7 @@ class STLProfile(object): streams.append(STLStream(name = i, packet = CScapyTRexPktBuilder(pkt_buffer = cap, vm = vm), - mode = STLTXSingleBurst(total_pkts = 1), + mode = STLTXSingleBurst(total_pkts = 1, percentage = 100), self_start = True if (i == 1) else False, isg = (ts_usec - last_ts_usec), # seconds to usec action_count = action_count, diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py index d4ad8bd2..496bea13 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py @@ -54,6 +54,10 @@ class RC(): s += format_text("\n{0}".format(x.data), 'bold') return s + def __iter__(self): + return self.rc_list.__iter__() + + def prn_func (self, msg, newline = True): if newline: print msg |