From 5a26a09958318032868d82a7d2d0207464e1af2d Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 12 Apr 2016 14:26:10 +0300 Subject: events API and RX core util. --- .gitignore | 4 + .../trex_control_plane/stl/console/trex_console.py | 25 +-- .../stl/trex_stl_lib/trex_stl_async_client.py | 6 +- .../stl/trex_stl_lib/trex_stl_client.py | 176 ++++++++++++++++++--- .../stl/trex_stl_lib/trex_stl_stats.py | 45 +++++- .../stl/trex_stl_lib/trex_stl_types.py | 5 + src/main_dpdk.cpp | 1 + src/stateless/cp/trex_api_class.h | 30 +++- src/stateless/cp/trex_stateless.cpp | 2 +- 9 files changed, 233 insertions(+), 61 deletions(-) diff --git a/.gitignore b/.gitignore index 0b02fb21..80456922 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,10 @@ scripts/automation/trex_control_plane/doc/_build/* scripts/automation/trex_control_plane/doc_stl/_build/* scripts/a.pcap +scripts/exp/stl_vm_split_client_var.erf-0.erf +scripts/exp/stl_vm_split_flow_var_big_range.erf-0.erf +scripts/exp/stl_vm_split_flow_var_inc.erf-0.erf +scripts/exp/stl_vm_split_flow_var_small_range.erf-0.erf scripts/exp/http1_with_option_ipv6.pcap scripts/exp/http1_with_option.pcap scripts/stl/exportedFile.pcap diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py index aade572f..94cb4009 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -538,28 +538,7 @@ class TRexConsole(TRexGeneralCmd): def do_events (self, line): '''shows events recieved from server\n''' - - x = parsing_opts.ArgumentPack(['-c','--clear'], - {'action' : "store_true", - 'default': False, - 'help': "clear the events log"}) - - parser = parsing_opts.gen_parser(self, - "events", - self.do_events.__doc__, - x) - - opts = parser.parse_args(line.split()) - if opts is None: - return - - events = self.stateless_client.get_events() - for ev in events: - print(ev) - - if opts.clear: - self.stateless_client.clear_events() - print(format_text("\n\nEvent log was cleared\n\n")) + return self.stateless_client.get_events_line(line) def complete_profile(self, text, line, begidx, endidx): @@ -591,7 +570,7 @@ class TRexConsole(TRexGeneralCmd): info = self.stateless_client.get_connection_info() exe = './trex-console --top -t -q -s {0} -p {1} --async_port {2}'.format(info['server'], info['sync_port'], info['async_port']) - cmd = ['/usr/bin/xterm', '-geometry', '111x47', '-sl', '0', '-title', 'trex_tui', '-e', exe] + cmd = ['/usr/bin/xterm', '-geometry', '111x48', '-sl', '0', '-title', 'trex_tui', '-e', exe] self.terminal = subprocess.Popen(cmd) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py index 0f0fe83e..022077a9 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py @@ -275,18 +275,18 @@ class CTRexAsyncClient(): # stats if name == "trex-global": - self.event_handler.handle_async_stats_update(data, baseline) + self.event_handler.on_async_stats_update(data, baseline) # events elif name == "trex-event": - self.event_handler.handle_async_event(type, data) + self.event_handler.on_async_event(type, data) # barriers elif name == "trex-barrier": self.handle_async_barrier(type, data) elif name == "flow_stats": - self.event_handler.handle_async_rx_stats_event(data, baseline) + self.event_handler.on_async_rx_stats_event(data, baseline) else: pass diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 1c4984eb..120f4d10 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -125,8 +125,26 @@ class DefaultLogger(LoggerApi): ############################ ############################# ############################ ############################# +# an event +class Event(object): + + def __init__ (self, origin, ev_type, msg): + self.origin = origin + self.ev_type = ev_type + self.msg = msg + + self.ts = datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S') + + def __str__ (self): + + prefix = "[{:^}][{:^}]".format(self.origin, self.ev_type) + + return "{:<10} - {:18} - {:}".format(self.ts, prefix, format_text(self.msg, 'bold')) + + # handles different async events given to the client -class AsyncEventHandler(object): +class EventsHandler(object): + def __init__ (self, client): self.client = client @@ -136,31 +154,41 @@ class AsyncEventHandler(object): # public functions - def get_events (self): - return self.events + def get_events (self, ev_type_filter = None): + if ev_type_filter: + return [ev for ev in self.events if ev.ev_type in listify(ev_type_filter)] + else: + return [ev for ev in self.events] def clear_events (self): self.events = [] + def log_warning (self, msg, show = True): + self.__add_event_log('local', 'warning', msg, show) + + + # events called internally + def on_async_dead (self): if self.client.connected: msg = 'Lost connection to server' - self.__add_event_log(msg, 'local', True) + self.__add_event_log('local', 'info', msg, True) self.client.connected = False def on_async_alive (self): pass + - def handle_async_rx_stats_event (self, data, baseline): + def on_async_rx_stats_event (self, data, baseline): self.client.flow_stats.update(data, baseline) # handles an async stats update from the subscriber - def handle_async_stats_update(self, dump_data, baseline): + def on_async_stats_update(self, dump_data, baseline): global_stats = {} port_stats = {} @@ -189,8 +217,9 @@ class AsyncEventHandler(object): self.client.ports[port_id].port_stats.update(data, baseline) + # dispatcher for server async events (port started, port stopped and etc.) - def handle_async_event (self, type, data): + def on_async_event (self, type, data): # DP stopped show_event = False @@ -263,7 +292,7 @@ class AsyncEventHandler(object): return - self.__add_event_log(ev, 'server', show_event) + self.__add_event_log('server', 'info', ev, show_event) # private functions @@ -296,19 +325,12 @@ class AsyncEventHandler(object): # add event to log - def __add_event_log (self, msg, ev_type, show = False): - - if ev_type == "server": - prefix = "[server]" - elif ev_type == "local": - prefix = "[local]" - - ts = time.time() - st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') - self.events.append("{:<10} - {:^8} - {:}".format(st, prefix, format_text(msg, 'bold'))) + def __add_event_log (self, origin, ev_type, msg, show = False): + event = Event(origin, ev_type, msg) + self.events.append(event) if show: - self.logger.async_log(format_text("\n\n{:^8} - {:}".format(prefix, format_text(msg, 'bold')))) + self.logger.async_log("\n\n{0}".format(str(event))) @@ -452,7 +474,7 @@ class STLClient(object): self) # async event handler manager - self.event_handler = AsyncEventHandler(self) + self.event_handler = EventsHandler(self) # async subscriber level self.async_client = CTRexAsyncClient(server, @@ -472,7 +494,8 @@ class STLClient(object): self.global_stats = trex_stl_stats.CGlobalStats(self.connection_info, self.server_version, - self.ports) + self.ports, + self.event_handler) self.flow_stats = trex_stl_stats.CRxStats(self.ports) @@ -482,7 +505,7 @@ class STLClient(object): # API classes - self.api_vers = [ {'type': 'core', 'major': 1, 'minor':0 } + self.api_vers = [ {'type': 'core', 'major': 1, 'minor':1 } ] self.api_h = {'core': None} @@ -1155,9 +1178,58 @@ class STLClient(object): return self.__get_stats(ports) - # return all async events - def get_events (self): - return self.event_handler.get_events() + + def get_events (self, ev_type_filter = None): + """ + returns all the logged events + + :parameters: + ev_type_filter - 'info', 'warning' or a list of those + default is no filter + + :return: + logged events + + :raises: + None + + """ + return self.event_handler.get_events(ev_type_filter) + + + def get_warnings (self): + """ + returns all the warnings logged events + + :parameters: + None + + :return: + warning logged events + + :raises: + None + + """ + return self.get_events(ev_type_filter = 'warning') + + + def get_info (self): + """ + returns all the info logged events + + :parameters: + None + + :return: + warning logged events + + :raises: + None + + """ + return self.get_events(ev_type_filter = 'info') + # get port(s) info as a list of dicts @__api_check(True) @@ -2212,7 +2284,7 @@ class STLClient(object): mask = self.__get_mask_keys(**self.__filter_namespace_args(opts, trex_stl_stats.ALL_STATS_OPTS)) if not mask: # set to show all stats if no filter was given - mask = trex_stl_stats.ALL_STATS_OPTS + mask = trex_stl_stats.COMPACT stats_opts = common.list_intersect(trex_stl_stats.ALL_STATS_OPTS, mask) @@ -2371,3 +2443,55 @@ class STLClient(object): self.logger.log("") + + @__console + def get_events_line (self, line): + '''shows events recieved from server\n''' + + x = [parsing_opts.ArgumentPack(['-c','--clear'], + {'action' : "store_true", + 'default': False, + 'help': "clear the events log"}), + + parsing_opts.ArgumentPack(['-i','--info'], + {'action' : "store_true", + 'default': False, + 'help': "show info events"}), + + parsing_opts.ArgumentPack(['-w','--warn'], + {'action' : "store_true", + 'default': False, + 'help': "show warning events"}), + + ] + + + parser = parsing_opts.gen_parser(self, + "events", + self.get_events_line.__doc__, + *x) + + opts = parser.parse_args(line.split()) + if opts is None: + return + + + ev_type_filter = [] + + if opts.info: + ev_type_filter.append('info') + + if opts.warn: + ev_type_filter.append('warning') + + if not ev_type_filter: + ev_type_filter = None + + events = self.get_events(ev_type_filter) + for ev in events: + self.logger.log(ev) + + if opts.clear: + self.clear_events() + self.logger.log(format_text("\nEvent log was cleared\n")) + 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 dd597648..48111433 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 @@ -91,6 +91,30 @@ def calculate_diff_raw (samples): return total +# a simple object to keep a watch over a field +class WatchedField(object): + + def __init__ (self, name, suffix, high_th, low_th, events_handler): + self.name = name + self.suffix = suffix + self.high_th = high_th + self.low_th = low_th + self.events_handler = events_handler + + self.hot = False + self.current = None + + def update (self, value): + if value > self.high_th and not self.hot: + self.events_handler.log_warning("{0} is high: {1}{2}".format(self.name, value, self.suffix)) + self.hot = True + + if value < self.low_th and self.hot: + self.hot = False + + self.current = value + + class CTRexInfoGenerator(object): """ @@ -540,17 +564,24 @@ class CTRexStats(object): class CGlobalStats(CTRexStats): - def __init__(self, connection_info, server_version, ports_dict_ref): + def __init__(self, connection_info, server_version, ports_dict_ref, events_handler): super(CGlobalStats, self).__init__() + self.connection_info = connection_info - self.server_version = server_version - self._ports_dict = ports_dict_ref + self.server_version = server_version + self._ports_dict = ports_dict_ref + self.events_handler = events_handler + + self.watched_cpu_util = WatchedField('CPU util.', '%', 85, 60, events_handler) + self.watched_rx_cpu_util = WatchedField('RX core util.', '%', 85, 60, events_handler) def get_stats (self): stats = {} # absolute - stats['cpu_util'] = self.get("m_cpu_util") + stats['cpu_util'] = self.get("m_cpu_util") + stats['rx_cpu_util'] = self.get("m_rx_cpu_util") + stats['tx_bps'] = self.get("m_tx_bps") stats['tx_pps'] = self.get("m_tx_pps") @@ -576,6 +607,9 @@ class CGlobalStats(CTRexStats): # simple... self.latest_stats = snapshot + self.watched_cpu_util.update(snapshot['m_cpu_util']) + self.watched_rx_cpu_util.update(snapshot['m_rx_cpu_util']) + return True @@ -588,6 +622,9 @@ class CGlobalStats(CTRexStats): ("cpu_util", "{0}% {1}".format( format_threshold(self.get("m_cpu_util"), [85, 100], [0, 85]), self.get_trend_gui("m_cpu_util", use_raw = True))), + ("rx_cpu_util", "{0}% {1}".format( format_threshold(self.get("m_rx_cpu_util"), [85, 100], [0, 85]), + self.get_trend_gui("m_rx_cpu_util", use_raw = True))), + (" ", ""), ("total_tx_L2", "{0} {1}".format( self.get("m_tx_bps", format=True, suffix="b/sec"), 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 f3ac5c65..f6718fda 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 @@ -140,6 +140,11 @@ def verify_exclusive_arg (args_list): if not (len(list(filter(lambda x: x is not None, args_list))) == 1): raise STLError('exactly one parameter from {0} should be provided'.format(args_list)) +def listify (x): + if isinstance(x, list): + return x + else: + return [x] # shows as 'N/A', but does not let any compares for user to not mistake in automation class StatNotAvailable(object): diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 652e6947..131e87ac 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -2368,6 +2368,7 @@ void CGlobalStats::dump_json(std::string & json, bool baseline,uint32_t stats_ti #define GET_FIELD_PORT(p,f) get_field_port(p,std::string(#f),lp->f) json+=GET_FIELD(m_cpu_util); + json+=GET_FIELD(m_rx_cpu_util); json+=GET_FIELD(m_platform_factor); json+=GET_FIELD(m_tx_bps); json+=GET_FIELD(m_rx_bps); diff --git a/src/stateless/cp/trex_api_class.h b/src/stateless/cp/trex_api_class.h index 78933d23..748d1478 100644 --- a/src/stateless/cp/trex_api_class.h +++ b/src/stateless/cp/trex_api_class.h @@ -75,20 +75,42 @@ public: m_handler = utl_generate_random_str(seed, 8); } + std::string ver(int major, int minor) { + std::stringstream ss; + ss << major << "." << minor; + return ss.str(); + } + + std::string get_server_ver() { + return ver(m_major, m_major); + } + std::string & verify_api(int major, int minor) { std::stringstream ss; ss << "API type '" << type_to_name(m_type) << "': "; - + assert(m_type < API_CLASS_TYPE_MAX); + bool fail = false; + /* for now a simple major check */ if (major < m_major) { - ss << "server has a major newer API version - server: '" << m_major << "', client: '" << major << "'"; - throw TrexAPIException(ss.str()); + ss << "server has a newer major API version"; + fail = true; } if (major > m_major) { - ss << "server has an older API version - server: '" << m_major << "', client: '" << major << "'"; + ss << "server has an older major API version"; + fail = true; + } + + if (minor > m_minor) { + ss << "client revision API is newer than server"; + fail = true; + } + + if (fail) { + ss << " - server: '" << get_server_ver() << "', client: '" << ver(major, minor) << "'"; throw TrexAPIException(ss.str()); } diff --git a/src/stateless/cp/trex_stateless.cpp b/src/stateless/cp/trex_stateless.cpp index f6f81b96..c86c5f65 100644 --- a/src/stateless/cp/trex_stateless.cpp +++ b/src/stateless/cp/trex_stateless.cpp @@ -54,7 +54,7 @@ TrexStateless::TrexStateless(const TrexStatelessCfg &cfg) { m_publisher = cfg.m_publisher; /* API core version */ - m_api_classes[APIClass::API_CLASS_TYPE_CORE].init(APIClass::API_CLASS_TYPE_CORE, 1, 0); + m_api_classes[APIClass::API_CLASS_TYPE_CORE].init(APIClass::API_CLASS_TYPE_CORE, 1, 1); } /** -- cgit 1.2.3-korg From 2c00ae277f8c50ebeec17f0e059b17f2b4ba2a07 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Tue, 12 Apr 2016 16:45:23 +0300 Subject: Temporary fix for trex-199 (xl170 counters stuck at 4G) --- src/dpdk22/drivers/net/i40e/i40e_ethdev.c | 15 ++++++++++ src/dpdk22/lib/librte_ether/rte_ethdev.c | 17 +++++++++++ src/main_dpdk.cpp | 49 +++++++++++++++++++------------ 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/dpdk22/drivers/net/i40e/i40e_ethdev.c b/src/dpdk22/drivers/net/i40e/i40e_ethdev.c index dff4ec3c..e504e13c 100644 --- a/src/dpdk22/drivers/net/i40e/i40e_ethdev.c +++ b/src/dpdk22/drivers/net/i40e/i40e_ethdev.c @@ -2114,6 +2114,21 @@ i40e_trex_fdir_stats_get(struct rte_eth_dev *dev, uint32_t *stats, uint32_t star } } +// TREX_PATCH +void +i40e_trex_fdir_stats_reset(struct rte_eth_dev *dev, uint32_t *stats, uint32_t start, uint32_t len) +{ + int i; + struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private); + + for (i = 0; i < len; i++) { + if (stats) { + stats[i] = I40E_READ_REG(hw, I40E_GLQF_PCNT(i + start)); + } + I40E_WRITE_REG(hw, I40E_GLQF_PCNT(i + start), 0xffffffff); + } +} + /* Get all statistics of a port */ static void i40e_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) diff --git a/src/dpdk22/lib/librte_ether/rte_ethdev.c b/src/dpdk22/lib/librte_ether/rte_ethdev.c index 383ad120..44b4b640 100644 --- a/src/dpdk22/lib/librte_ether/rte_ethdev.c +++ b/src/dpdk22/lib/librte_ether/rte_ethdev.c @@ -1462,6 +1462,23 @@ rte_eth_fdir_stats_get(uint8_t port_id, uint32_t *stats, uint32_t start, uint32_ return 0; } +// TREX_PATCH +// zero statistics counters, starting from start, for len counters. +int +rte_eth_fdir_stats_reset(uint8_t port_id, uint32_t *stats, uint32_t start, uint32_t len) +{ + struct rte_eth_dev *dev; + + RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL); + + dev = &rte_eth_devices[port_id]; + + // Only xl710 support this + i40e_trex_fdir_stats_reset(dev, stats, start, len); + + return 0; +} + int rte_eth_stats_get(uint8_t port_id, struct rte_eth_stats *stats) { diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 131e87ac..e20221d9 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -144,7 +144,7 @@ public: virtual bool hw_rx_stat_supported(){return false;} virtual int get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, uint32_t *prev_pkts, uint32_t *bytes, uint32_t *prev_bytes , int min, int max) {return -1;} - virtual int reset_rx_stats(CPhyEthIF * _if, uint32_t *stats) {return 0;} + virtual void reset_rx_stats(CPhyEthIF * _if, uint32_t *stats, int min, int len) {} virtual int dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd) { return -1;} virtual int get_stat_counters_num() {return 0;} virtual int get_rx_stat_capabilities() {return 0;} @@ -325,7 +325,7 @@ public: } virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats); virtual void clear_extended_stats(CPhyEthIF * _if); - virtual int reset_rx_stats(CPhyEthIF * _if, uint32_t *stats); + virtual void reset_rx_stats(CPhyEthIF * _if, uint32_t *stats, int min, int len); virtual int get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, uint32_t *prev_pkts, uint32_t *bytes, uint32_t *prev_bytes, int min, int max); virtual int dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd); virtual int get_stat_counters_num() {return MAX_FLOW_STATS;} @@ -4009,9 +4009,7 @@ static CGlobalTRex g_trex; int CPhyEthIF::reset_hw_flow_stats() { if (get_ex_drv()->hw_rx_stat_supported()) { - if (get_ex_drv()->reset_rx_stats(this, m_stats.m_fdir_prev_pkts) < 0) { - return -1; - } + get_ex_drv()->reset_rx_stats(this, m_stats.m_fdir_prev_pkts, 0, MAX_FLOW_STATS); } else { g_trex.m_rx_sl.reset_rx_stats(get_port_id()); } @@ -4047,6 +4045,8 @@ int CPhyEthIF::get_flow_stats(rx_per_flow_t *rx_stats, tx_per_flow_t *tx_stats, } m_stats.m_rx_per_flow_pkts[i] = 0; m_stats.m_rx_per_flow_bytes[i] = 0; + get_ex_drv()->reset_rx_stats(this, &m_stats.m_fdir_prev_pkts[i], i, 1); + } if (tx_stats != NULL) { tx_stats[i - min] = g_trex.clear_flow_tx_stats(m_port_id, i); @@ -5059,6 +5059,8 @@ void CTRexExtendedDriverBase40G::add_del_rules(enum rte_filter_op op, uint8_t po } } +extern "C" int rte_eth_fdir_stats_reset(uint8_t port_id, uint32_t *stats, uint32_t start, uint32_t len); + // type - rule type. Currently we only support rules in IP ID. // proto - Packet protocol: UDP or TCP // id - Counter id in HW. We assume it is in the range 0..MAX_FLOW_STATS @@ -5086,6 +5088,7 @@ int CTRexExtendedDriverBase40G::configure_rx_filter_rules_statfull(CPhyEthIF * _ uint16_t hops = get_rx_check_hops(); int i; + rte_eth_fdir_stats_reset(port_id, NULL, 0, 1); for (i = 0; i < 2; i++) { uint8_t ttl = TTL_RESERVE_DUPLICATE - i - hops; add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, ttl, 0, MAIN_DPDK_RX_Q, 0); @@ -5109,18 +5112,24 @@ int CTRexExtendedDriverBase40G::configure_rx_filter_rules(CPhyEthIF * _if) { } } -int CTRexExtendedDriverBase40G::reset_rx_stats(CPhyEthIF * _if, uint32_t *stats) { - uint32_t diff_stats[MAX_FLOW_STATS]; - uint32_t diff_bytes[MAX_FLOW_STATS]; - - // The HW counters start from some random values. The driver give us the diffs from previous, - // each time we do get_rx_stats. We need to make one first call, at system startup, - // and ignore the returned diffs - return get_rx_stats(_if, diff_stats, stats, diff_bytes, NULL, 0, MAX_FLOW_STATS - 1); +void CTRexExtendedDriverBase40G::reset_rx_stats(CPhyEthIF * _if, uint32_t *stats, int min, int len) { + uint32_t port_id = _if->get_port_id(); + // Since the xl710 fdir counter stuck at 0xffffffff issue, we zero the HW counters, so should zero here also. + for (int i = min; i <= min - len + 1; i++) { + uint32_t rule_id = (port_id % m_if_per_card) * MAX_FLOW_STATS + i; + stats[i - min] = 0; + // Since flow dir counters are not wrapped around as promised in the data sheet, but rather get stuck at 0xffffffff + // we reset the HW value + rte_eth_fdir_stats_reset(port_id, NULL, rule_id, 1); + } } // instead of adding this to rte_ethdev.h extern "C" int rte_eth_fdir_stats_get(uint8_t port_id, uint32_t *stats, uint32_t start, uint32_t len); +// we read every 0.5 second. We want to catch the counter when it approach the maximum (where it will stuck, +// and we will start losing packets). +const uint32_t X710_FDIR_RESET_THRESHOLD = 0xffffffff - 1000000000/8/64*40; + // get rx stats on _if, between min and max // prev_pkts should be the previous values read from the hardware. // Getting changed to be equal to current HW values. @@ -5136,13 +5145,17 @@ int CTRexExtendedDriverBase40G::get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, ui rte_eth_fdir_stats_get(port_id, hw_stats, start, len); for (int i = loop_start; i < loop_start + len; i++) { - if (hw_stats[i - min] >= prev_pkts[i]) { - pkts[i] = (uint64_t)(hw_stats[i - min] - prev_pkts[i]); + if (unlikely(hw_stats[i - min] > X710_FDIR_RESET_THRESHOLD)) { + // read again, and reset. Trying to lose minimal amount of packets. + // better solution is on its way - see trex-199 for details + uint32_t counter; + rte_eth_fdir_stats_reset(port_id, &counter, start + i, 1); + pkts[i] = counter - prev_pkts[i]; + prev_pkts[i] = 0; } else { - // Wrap around - pkts[i] = (uint64_t)((hw_stats[i - min] + ((uint64_t)1 << 32)) - prev_pkts[i]); + pkts[i] = hw_stats[i - min] - prev_pkts[i]; + prev_pkts[i] = hw_stats[i - min]; } - prev_pkts[i] = hw_stats[i - min]; bytes[i] = 0; } -- cgit 1.2.3-korg From 87bac1abebe2f5a853e32301a68b9f6adf97de99 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 12 Apr 2016 18:01:43 +0300 Subject: added checks for warnings on examples --- .../trex_control_plane/stl/console/trex_console.py | 3 +- .../stl/examples/stl_bi_dir_flows.py | 44 ++++---- .../stl/examples/stl_bi_dir_flows1.py | 114 --------------------- .../stl/examples/stl_flow_stats.py | 18 ++-- .../trex_control_plane/stl/examples/stl_imix.py | 7 +- .../stl/examples/stl_simple_burst.py | 31 +++--- .../stl/trex_stl_lib/trex_stl_stats.py | 7 +- scripts/exp/stl_vm_split_client_var.erf-0.erf | Bin 11000 -> 11000 bytes .../exp/stl_vm_split_flow_var_big_range.erf-0.erf | Bin 11000 -> 11000 bytes scripts/exp/stl_vm_split_flow_var_inc.erf-0.erf | Bin 11000 -> 11000 bytes .../stl_vm_split_flow_var_small_range.erf-0.erf | Bin 11000 -> 11000 bytes 11 files changed, 67 insertions(+), 157 deletions(-) delete mode 100644 scripts/automation/trex_control_plane/stl/examples/stl_bi_dir_flows1.py diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py index 94cb4009..cb266505 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -572,7 +572,8 @@ class TRexConsole(TRexGeneralCmd): exe = './trex-console --top -t -q -s {0} -p {1} --async_port {2}'.format(info['server'], info['sync_port'], info['async_port']) cmd = ['/usr/bin/xterm', '-geometry', '111x48', '-sl', '0', '-title', 'trex_tui', '-e', exe] - self.terminal = subprocess.Popen(cmd) + # detach child + self.terminal = subprocess.Popen(cmd, preexec_fn = os.setpgrp) return diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_bi_dir_flows.py b/scripts/automation/trex_control_plane/stl/examples/stl_bi_dir_flows.py index d4661b1a..9977fa3e 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_bi_dir_flows.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_bi_dir_flows.py @@ -38,7 +38,7 @@ def create_pkt (size, direction): vm = vm) -def simple_burst (): +def simple_burst (port_a, port_b, pkt_size, rate): # create client @@ -50,11 +50,11 @@ def simple_burst (): #c.set_verbose("high") # create two streams - s1 = STLStream(packet = create_pkt(200, 0), + s1 = STLStream(packet = create_pkt(pkt_size, 0), mode = STLTXCont(pps = 100)) # second stream with a phase of 1ms (inter stream gap) - s2 = STLStream(packet = create_pkt(200, 1), + s2 = STLStream(packet = create_pkt(pkt_size, 1), isg = 1000, mode = STLTXCont(pps = 100)) @@ -62,36 +62,41 @@ def simple_burst (): # connect to server c.connect() - # prepare our ports (my machine has 0 <--> 1 with static route) - c.reset(ports = [0, 1]) + # prepare our ports + c.reset(ports = [port_a, port_b]) # add both streams to ports - c.add_streams(s1, ports = [0]) - c.add_streams(s2, ports = [1]) + c.add_streams(s1, ports = [port_a]) + c.add_streams(s2, ports = [port_b]) # clear the stats before injecting c.clear_stats() - # choose rate and start traffic for 10 seconds on 5 mpps - print("Running 100 Mbps on ports 0, 1 for 10 seconds...") - c.start(ports = [0, 1], mult = "100mbps", duration = 10) + # here we multiply the traffic lineaer to whatever given in rate + print("Running {:} on ports {:}, {:} for 10 seconds...".format(rate, port_a, port_b)) + c.start(ports = [port_a, port_b], mult = rate, duration = 10) # block until done - c.wait_on_traffic(ports = [0, 1]) + c.wait_on_traffic(ports = [port_a, port_b]) # read the stats after the test stats = c.get_stats() - print(json.dumps(stats[0], indent = 4, separators=(',', ': '), sort_keys = True)) - print(json.dumps(stats[1], indent = 4, separators=(',', ': '), sort_keys = True)) + print(json.dumps(stats[port_a], indent = 4, separators=(',', ': '), sort_keys = True)) + print(json.dumps(stats[port_b], indent = 4, separators=(',', ': '), sort_keys = True)) - lost_a = stats[0]["opackets"] - stats[1]["ipackets"] - lost_b = stats[1]["opackets"] - stats[0]["ipackets"] + lost_a = stats[port_a]["opackets"] - stats[port_b]["ipackets"] + lost_b = stats[port_b]["opackets"] - stats[port_a]["ipackets"] - print("\npackets lost from 0 --> 1: {0} pkts".format(lost_a)) - print("packets lost from 1 --> 0: {0} pkts".format(lost_b)) + print("\npackets lost from {0} --> {1}: {2} pkts".format(port_a, port_b, lost_a)) + print("packets lost from {0} --> {1}: {2} pkts".format(port_b, port_a, lost_b)) - if (lost_a == 0) and (lost_b == 0): + if c.get_warnings(): + print("\n\n*** test had warnings ****\n\n") + for w in c.get_warnings(): + print(w) + + if (lost_a == 0) and (lost_b == 0) and not c.get_warnings(): passed = True else: passed = False @@ -108,7 +113,6 @@ def simple_burst (): else: print("\nTest has failed :-(\n") -while True: # run the tests - simple_burst() +simple_burst(0, 3, 64, "10gbps") diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_bi_dir_flows1.py b/scripts/automation/trex_control_plane/stl/examples/stl_bi_dir_flows1.py deleted file mode 100644 index c9cab8e9..00000000 --- a/scripts/automation/trex_control_plane/stl/examples/stl_bi_dir_flows1.py +++ /dev/null @@ -1,114 +0,0 @@ -import stl_path -from trex_stl_lib.api import * - -import time -import json - -# simple packet creation -def create_pkt (size, direction): - - ip_range = {'src': {'start': "10.0.0.1", 'end': "10.0.0.254"}, - 'dst': {'start': "8.0.0.1", 'end': "8.0.0.254"}} - - if (direction == 0): - src = ip_range['src'] - dst = ip_range['dst'] - else: - src = ip_range['dst'] - dst = ip_range['src'] - - vm = [ - # src - STLVmFlowVar(name="src",min_value=src['start'],max_value=src['end'],size=4,op="inc"), - STLVmWrFlowVar(fv_name="src",pkt_offset= "IP.src"), - - # dst - STLVmFlowVar(name="dst",min_value=dst['start'],max_value=dst['end'],size=4,op="inc"), - STLVmWrFlowVar(fv_name="dst",pkt_offset= "IP.dst"), - - # checksum - STLVmFixIpv4(offset = "IP") - ] - - - base = Ether()/IP()/UDP() - pad = max(0, size-len(base)) * 'x' - - return STLPktBuilder(pkt = base/pad, - vm = vm) - - -def simple_burst (): - - - # create client - c = STLClient() - passed = True - - try: - # turn this on for some information - #c.set_verbose("high") - - # create two streams - s1 = STLStream(packet = create_pkt(200, 0), - mode = STLTXCont(pps = 100)) - - # second stream with a phase of 1ms (inter stream gap) - s2 = STLStream(packet = create_pkt(200, 1), - isg = 1000, - mode = STLTXCont(pps = 100)) - - - # connect to server - c.connect() - - # prepare our ports (my machine has 0 <--> 1 with static route) - c.reset(ports = [2, 3]) - - # add both streams to ports - c.add_streams(s1, ports = [2]) - c.add_streams(s2, ports = [3]) - - # clear the stats before injecting - c.clear_stats() - - # choose rate and start traffic for 10 seconds on 5 mpps - print("Running 5 Mpps on ports 0, 1 for 10 seconds...") - c.start(ports = [2, 3], mult = "5mpps", duration = 10) - - # block until done - c.wait_on_traffic(ports = [2, 3]) - - # read the stats after the test - stats = c.get_stats() - - print(json.dumps(stats[2], indent = 4, separators=(',', ': '), sort_keys = True)) - print(json.dumps(stats[3], indent = 4, separators=(',', ': '), sort_keys = True)) - - lost_a = stats[2]["opackets"] - stats[3]["ipackets"] - lost_b = stats[3]["opackets"] - stats[2]["ipackets"] - - print("\npackets lost from 0 --> 1: {0} pkts".format(lost_a)) - print("packets lost from 1 --> 0: {0} pkts".format(lost_b)) - - if (lost_a == 0) and (lost_b == 0): - passed = True - else: - passed = False - - except STLError as e: - passed = False - print(e) - - finally: - c.disconnect() - - if passed: - print("\nTest has passed :-)\n") - else: - print("\nTest has failed :-(\n") - -while True : - # run the tests - simple_burst() - diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py b/scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py index d938852e..ed4902fa 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py @@ -4,7 +4,7 @@ from trex_stl_lib.api import * import time import pprint -def rx_example (tx_port, rx_port, burst_size): +def rx_example (tx_port, rx_port, burst_size, bw): print("\nGoing to inject {0} packets on port {1} - checking RX stats on port {2}\n".format(burst_size, tx_port, rx_port)) @@ -19,9 +19,7 @@ def rx_example (tx_port, rx_port, burst_size): packet = pkt, flow_stats = STLFlowStats(pg_id = 5), mode = STLTXSingleBurst(total_pkts = total_pkts, - #pps = total_pkts - percentage = 80 - )) + percentage = bw)) # connect to server c.connect() @@ -36,7 +34,7 @@ def rx_example (tx_port, rx_port, burst_size): for i in range(0, 10): print("\nStarting iteration: {0}:".format(i)) - rc = rx_iteration(c, tx_port, rx_port, total_pkts, pkt.get_pkt_len()) + rc = rx_iteration(c, tx_port, rx_port, total_pkts, pkt.get_pkt_len(), bw) if not rc: passed = False break @@ -55,7 +53,7 @@ def rx_example (tx_port, rx_port, burst_size): print("\nTest has failed :-(\n") # RX one iteration -def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len): +def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw): c.clear_stats() @@ -71,6 +69,12 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len): tx_bytes = flow_stats['tx_bytes'].get(tx_port, 0) rx_pkts = flow_stats['rx_pkts'].get(rx_port, 0) + if c.get_warnings(): + print("\n\n*** test had warnings ****\n\n") + for w in c.get_warnings(): + print(w) + return False + if tx_pkts != total_pkts: print("TX pkts mismatch - got: {0}, expected: {1}".format(tx_pkts, total_pkts)) pprint.pprint(flow_stats) @@ -95,5 +99,5 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len): return True # run the tests -rx_example(tx_port = 1, rx_port = 2, burst_size = 500000) +rx_example(tx_port = 1, rx_port = 2, burst_size = 500000, bw = 50) diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_imix.py b/scripts/automation/trex_control_plane/stl/examples/stl_imix.py index bc7990aa..46d86b2b 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_imix.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_imix.py @@ -83,7 +83,12 @@ def imix_test (server, mult): print("\npackets lost from {0} --> {1}: {2:,} pkts".format(dir_0, dir_0, lost_0)) print("packets lost from {0} --> {1}: {2:,} pkts".format(dir_1, dir_1, lost_1)) - if (lost_0 <= 0) and (lost_1 <= 0): # less or equal because we might have incoming arps etc. + if c.get_warnings(): + print("\n\n*** test had warnings ****\n\n") + for w in c.get_warnings(): + print(w) + + if (lost_0 <= 0) and (lost_1 <= 0) and not c.get_warnings(): # less or equal because we might have incoming arps etc. passed = True else: passed = False diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_simple_burst.py b/scripts/automation/trex_control_plane/stl/examples/stl_simple_burst.py index 29341674..4bd9fd4c 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_simple_burst.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_simple_burst.py @@ -3,47 +3,49 @@ from trex_stl_lib.api import * import time -def simple_burst (): +def simple_burst (port_a, port_b, pkt_size, burst_size, rate): # create client c = STLClient() passed = True try: - pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/IP()/'a_payload_example') + pkt_base = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/IP() + pad = max(0, pkt_size - len(pkt_base)) * 'x' + pkt = STLPktBuilder(pkt = pkt_base / pad) # create two bursts and link them s1 = STLStream(name = 'A', packet = pkt, - mode = STLTXSingleBurst(total_pkts = 5000), + mode = STLTXSingleBurst(total_pkts = burst_size), next = 'B') s2 = STLStream(name = 'B', self_start = False, packet = pkt, - mode = STLTXSingleBurst(total_pkts = 3000)) + mode = STLTXSingleBurst(total_pkts = burst_size)) # connect to server c.connect() # prepare our ports - c.reset(ports = [0, 3]) + c.reset(ports = [port_a, port_b]) # add both streams to ports - stream_ids = c.add_streams([s1, s2], ports = [0, 3]) + stream_ids = c.add_streams([s1, s2], ports = [port_a, port_b]) # run 5 times for i in range(1, 6): c.clear_stats() - c.start(ports = [0, 3], mult = "1gbps") - c.wait_on_traffic(ports = [0, 3]) + c.start(ports = [port_a, port_b], mult = rate) + c.wait_on_traffic(ports = [port_a, port_b]) stats = c.get_stats() ipackets = stats['total']['ipackets'] print("Test iteration {0} - Packets Received: {1} ".format(i, ipackets)) - # (5000 + 3000) * 2 ports = 16,000 - if (ipackets != (16000)): + # two streams X 2 ports + if (ipackets != (burst_size * 2 * 2)): passed = False except STLError as e: @@ -53,12 +55,17 @@ def simple_burst (): finally: c.disconnect() - if passed: + if c.get_warnings(): + print("\n\n*** test had warnings ****\n\n") + for w in c.get_warnings(): + print(w) + + if passed and not c.get_warnings(): print("\nTest has passed :-)\n") else: print("\nTest has failed :-(\n") # run the tests -simple_burst() +simple_burst(0, 3, 256, 50000, "80%") 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 48111433..dea7c6d5 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 @@ -105,6 +105,9 @@ class WatchedField(object): self.current = None def update (self, value): + if value is None: + return + if value > self.high_th and not self.hot: self.events_handler.log_warning("{0} is high: {1}{2}".format(self.name, value, self.suffix)) self.hot = True @@ -607,8 +610,8 @@ class CGlobalStats(CTRexStats): # simple... self.latest_stats = snapshot - self.watched_cpu_util.update(snapshot['m_cpu_util']) - self.watched_rx_cpu_util.update(snapshot['m_rx_cpu_util']) + self.watched_cpu_util.update(snapshot.get('m_cpu_util')) + self.watched_rx_cpu_util.update(snapshot.get('m_rx_cpu_util')) return True diff --git a/scripts/exp/stl_vm_split_client_var.erf-0.erf b/scripts/exp/stl_vm_split_client_var.erf-0.erf index 25b2a2bd..f156bfe5 100644 Binary files a/scripts/exp/stl_vm_split_client_var.erf-0.erf and b/scripts/exp/stl_vm_split_client_var.erf-0.erf differ diff --git a/scripts/exp/stl_vm_split_flow_var_big_range.erf-0.erf b/scripts/exp/stl_vm_split_flow_var_big_range.erf-0.erf index d5ad29d8..9a386724 100644 Binary files a/scripts/exp/stl_vm_split_flow_var_big_range.erf-0.erf and b/scripts/exp/stl_vm_split_flow_var_big_range.erf-0.erf differ diff --git a/scripts/exp/stl_vm_split_flow_var_inc.erf-0.erf b/scripts/exp/stl_vm_split_flow_var_inc.erf-0.erf index a938b37c..e6b9b85b 100644 Binary files a/scripts/exp/stl_vm_split_flow_var_inc.erf-0.erf and b/scripts/exp/stl_vm_split_flow_var_inc.erf-0.erf differ diff --git a/scripts/exp/stl_vm_split_flow_var_small_range.erf-0.erf b/scripts/exp/stl_vm_split_flow_var_small_range.erf-0.erf index 94e493fe..0a13e822 100644 Binary files a/scripts/exp/stl_vm_split_flow_var_small_range.erf-0.erf and b/scripts/exp/stl_vm_split_flow_var_small_range.erf-0.erf differ -- cgit 1.2.3-korg From 071ef655638f6d6b93930404914bb70004d65c36 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Wed, 13 Apr 2016 10:19:03 +0300 Subject: Changed i40e pmd error messages from ERR to INFO --- src/dpdk22/drivers/net/i40e/i40e_ethdev.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dpdk22/drivers/net/i40e/i40e_ethdev.c b/src/dpdk22/drivers/net/i40e/i40e_ethdev.c index e504e13c..ae195683 100644 --- a/src/dpdk22/drivers/net/i40e/i40e_ethdev.c +++ b/src/dpdk22/drivers/net/i40e/i40e_ethdev.c @@ -8428,6 +8428,7 @@ i40e_dcb_hw_configure(struct i40e_pf *pf, * * Returns 0 on success, negative value on failure */ +//TREX_PATCH - changed all ERR to INFO in below func static int i40e_dcb_init_configure(struct rte_eth_dev *dev, bool sw_dcb) { @@ -8436,7 +8437,7 @@ i40e_dcb_init_configure(struct rte_eth_dev *dev, bool sw_dcb) int ret = 0; if ((pf->flags & I40E_FLAG_DCB) == 0) { - PMD_INIT_LOG(ERR, "HW doesn't support DCB"); + PMD_INIT_LOG(INFO, "HW doesn't support DCB"); return -ENOTSUP; } @@ -8478,7 +8479,7 @@ i40e_dcb_init_configure(struct rte_eth_dev *dev, bool sw_dcb) I40E_APP_PROTOID_FCOE; ret = i40e_set_dcb_config(hw); if (ret) { - PMD_INIT_LOG(ERR, "default dcb config fails." + PMD_INIT_LOG(INFO, "default dcb config fails." " err = %d, aq_err = %d.", ret, hw->aq.asq_last_status); return -ENOSYS; @@ -8497,12 +8498,12 @@ i40e_dcb_init_configure(struct rte_eth_dev *dev, bool sw_dcb) ret = i40e_init_dcb(hw); if (!ret) { if (hw->dcbx_status == I40E_DCBX_STATUS_DISABLED) { - PMD_INIT_LOG(ERR, "HW doesn't support" + PMD_INIT_LOG(INFO, "HW doesn't support" " DCBX offload."); return -ENOTSUP; } } else { - PMD_INIT_LOG(ERR, "DCBX configuration failed, err = %d," + PMD_INIT_LOG(INFO, "DCBX configuration failed, err = %d," " aq_err = %d.", ret, hw->aq.asq_last_status); return -ENOTSUP; -- cgit 1.2.3-korg From 0b39ec305e80999c7dbe36d4b0d3850b04709571 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 13 Apr 2016 11:22:26 +0300 Subject: support for partial port acquire on console --readonly, -a [ports], -f --- .../trex_control_plane/stl/console/trex_console.py | 65 ++++++++++++++++---- .../stl/trex_stl_lib/trex_stl_client.py | 70 +++++++++++++++++++--- .../stl/trex_stl_lib/trex_stl_port.py | 11 +++- .../stl/trex_stl_lib/trex_stl_stats.py | 2 +- 4 files changed, 125 insertions(+), 23 deletions(-) diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py index cb266505..a095541e 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -47,7 +47,7 @@ except: from functools import wraps -__version__ = "1.1" +__version__ = "2.0" # console custom logger class ConsoleLogger(LoggerApi): @@ -267,17 +267,19 @@ class TRexConsole(TRexGeneralCmd): if not self.stateless_client.is_connected(): self.prompt = "trex(offline)>" self.supported_rpc = None - return stop - if self.stateless_client.is_all_ports_acquired(): + elif not self.stateless_client.get_acquired_ports(): self.prompt = "trex(read-only)>" - return stop + elif self.stateless_client.is_all_ports_acquired(): + self.prompt = "trex>" - self.prompt = "trex>" + else: + self.prompt = "trex {0}>".format(self.stateless_client.get_acquired_ports()) return stop + def default(self, line): print("'{0}' is an unrecognized command. type 'help' or '?' for a list\n".format(line)) @@ -416,17 +418,35 @@ class TRexConsole(TRexGeneralCmd): ############### connect def do_connect (self, line): - '''Connects to the server\n''' + '''Connects to the server and acquire ports\n''' self.stateless_client.connect_line(line) + def help_connect (self): + self.do_connect("-h") def do_disconnect (self, line): '''Disconnect from the server\n''' self.stateless_client.disconnect_line(line) + + def do_acquire (self, line): + '''Acquire ports\n''' + + self.stateless_client.acquire_line(line) + def help_acquire (self): + self.do_acquire("-h") + + def do_release (self, line): + '''Release ports\n''' + + self.stateless_client.release_line(line) + + def help_release (self): + self.do_release("-h") + ############### start def complete_start(self, text, line, begidx, endidx): @@ -730,9 +750,24 @@ def setParserOptions(): default = False) - parser.add_argument("--no_acquire", dest="acquire", - action="store_false", help="Acquire all ports on connect. Default is: ON.", - default = True) + group = parser.add_mutually_exclusive_group() + + group.add_argument("-a", "--acquire", dest="acquire", + nargs = '+', + type = int, + help="Acquire ports on connect. default is all available ports", + default = None) + + group.add_argument("-r", "--readonly", dest="readonly", + action="store_true", + help="Starts console in a read only mode", + default = False) + + + parser.add_argument("-f", "--force", dest="force", + action="store_true", + help="Force acquire the requested ports", + default = False) parser.add_argument("--batch", dest="batch", nargs = 1, @@ -795,15 +830,19 @@ def main(): logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold')) return - if not options.tui and options.acquire: + if not options.tui and not options.readonly: try: # acquire all ports - stateless_client.acquire() + stateless_client.acquire(options.acquire, force = options.force) except STLError as e: logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold')) - logger.log(format_text("\nSwitching to read only mode - only few commands will be available", 'bold')) + + logger.log("\n*** Failed to acquire all required ports ***\n") + return + + if options.readonly: + logger.log(format_text("\nRead only mode - only few commands will be available", 'bold')) - # a script mode if options.batch: cont = run_script_file(options.batch[0], stateless_client) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 120f4d10..98f3fe3a 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -277,7 +277,7 @@ class EventsHandler(object): ev = "Port {0} was forcely taken by '{1}'".format(port_id, who) # call the handler - self.__async_event_port_forced_acquired(port_id) + self.__async_event_port_forced_acquired(port_id, who) show_event = True # server stopped @@ -316,8 +316,8 @@ class EventsHandler(object): self.client.ports[port_id].async_event_port_resumed() - def __async_event_port_forced_acquired (self, port_id): - self.client.ports[port_id].async_event_forced_acquired() + def __async_event_port_forced_acquired (self, port_id, who): + self.client.ports[port_id].async_event_forced_acquired(who) def __async_event_server_stopped (self): @@ -1000,7 +1000,8 @@ class STLClient(object): """ - return not (self.get_all_ports() == self.get_acquired_ports()) + return (self.get_all_ports() == self.get_acquired_ports()) + # is the client connected ? def is_connected (self): @@ -2023,11 +2024,11 @@ class STLClient(object): @__console def connect_line (self, line): - '''Connects to the TRex server''' - # define a parser + '''Connects to the TRex server and acquire ports''' parser = parsing_opts.gen_parser(self, "connect", self.connect_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL, parsing_opts.FORCE) opts = parser.parse_args(line.split()) @@ -2035,9 +2036,62 @@ class STLClient(object): if opts is None: return - # call the API self.connect() - self.acquire(force = opts.force) + self.acquire(ports = opts.ports, force = opts.force) + + # true means print time + return True + + @__console + def acquire_line (self, line): + '''Acquire ports\n''' + + # define a parser + parser = parsing_opts.gen_parser(self, + "acquire", + self.acquire_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL, + parsing_opts.FORCE) + + opts = parser.parse_args(line.split()) + + if opts is None: + return + + # call the API + ports = [x for x in opts.ports if x not in self.get_acquired_ports()] + if not ports: + self.logger.log("Port(s) {0} are already acquired\n".format(opts.ports)) + return + + self.acquire(ports = ports, force = opts.force) + + # true means print time + return True + + + # + @__console + def release_line (self, line): + '''Release ports\n''' + + parser = parsing_opts.gen_parser(self, + "release", + self.release_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + + opts = parser.parse_args(line.split()) + + if opts is None: + return + + # call the API + ports = [x for x in opts.ports if x in self.get_acquired_ports()] + if not ports: + self.logger.log("Port(s) {0} are not owned by you\n".format(opts.ports)) + return + + self.release(ports = ports) # true means print time return True 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 87f7b437..6f6f50b1 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 @@ -64,6 +64,8 @@ class Port(object): self.tx_stopped_ts = None self.has_rx_streams = False + self.owner = '' + def err(self, msg): return RC_ERR("port {0} : {1}\n".format(self.port_id, msg)) @@ -113,6 +115,11 @@ class Port(object): def is_paused (self): return (self.state == self.STATE_PAUSE) + def get_owner (self): + if self.is_acquired(): + return self.user + else: + return self.owner def sync(self): params = {"port_id": self.port_id} @@ -137,6 +144,7 @@ class Port(object): else: raise Exception("port {0}: bad state received from server '{1}'".format(self.port_id, port_state)) + self.owner = rc.data()['owner'] self.next_available_id = int(rc.data()['max_stream_id']) + 1 @@ -671,6 +679,7 @@ class Port(object): if not self.is_acquired(): self.state = self.STATE_TX - def async_event_forced_acquired (self): + def async_event_forced_acquired (self, who): self.handler = None + self.owner = who 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 dea7c6d5..f0ac5c33 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 @@ -747,7 +747,7 @@ class CPortStats(CTRexStats): state = format_text(state, 'bold') - return {"owner": self._port_obj.user if self._port_obj else "", + return {"owner": self._port_obj.get_owner() if self._port_obj else "", "state": "{0}".format(state), "--": " ", -- cgit 1.2.3-korg