summaryrefslogtreecommitdiffstats
path: root/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py')
-rwxr-xr-xscripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py218
1 files changed, 126 insertions, 92 deletions
diff --git a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
index 3b01560a..ecf6083b 100755
--- a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
+++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
@@ -13,6 +13,7 @@ from distutils.util import strtobool
from collections import deque, OrderedDict
from json import JSONDecoder
import traceback
+import signal
try:
from . import outer_packages
@@ -39,7 +40,7 @@ class CTRexClient(object):
This class defines the client side of the RESTfull interaction with TRex
"""
- def __init__(self, trex_host, max_history_size = 100, filtered_latency_amount = 0.001, trex_daemon_port = 8090, master_daemon_port = 8091, trex_zmq_port = 4500, verbose = False):
+ def __init__(self, trex_host, max_history_size = 100, filtered_latency_amount = 0.001, trex_daemon_port = 8090, master_daemon_port = 8091, trex_zmq_port = 4500, verbose = False, debug_image = False, trex_args = ''):
"""
Instantiate a TRex client object, and connecting it to listening daemon-server
@@ -72,6 +73,8 @@ class CTRexClient(object):
sets a verbose output on supported class method.
default value : **False**
+ trex_args : string
+ additional arguments passed to TRex. For example, "-w 3 --no-watchdog"
:raises:
socket errors, in case server could not be reached.
@@ -81,53 +84,23 @@ class CTRexClient(object):
self.trex_host = socket.gethostbyname(trex_host)
except: # give it another try
self.trex_host = socket.gethostbyname(trex_host)
- self.trex_daemon_port = trex_daemon_port
- self.master_daemon_port = master_daemon_port
- self.trex_zmq_port = trex_zmq_port
- self.seq = None
- self._last_sample = time.time()
- self.__default_user = get_current_user()
- self.verbose = verbose
- self.result_obj = CTRexResult(max_history_size, filtered_latency_amount)
- self.decoder = JSONDecoder()
- self.history = jsonrpclib.history.History()
- self.master_daemon_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = master_daemon_port )
- self.trex_server_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = trex_daemon_port )
- self.connect_master()
- self.connect_server()
-
-
- def connect_master(self):
- '''
- Connects to Master daemon via JsonRPC.
- This daemon controls TRex daemon server.
- Return true if success, false if fail
- '''
- try:
- print('Connecting to Master daemon @ %s ...' % self.master_daemon_path)
- self.master_daemon = jsonrpclib.Server(self.master_daemon_path, history = self.history)
- self.check_master_connectivity()
- print('Connected to Master daemon.')
- return True
- except Exception as e:
- print(e)
- return False
-
- def connect_server(self):
- '''
- Connects to TRex daemon server via JsonRPC.
- This daemon controls TRex. (start/stop)
- Return true if success, false if fail
- '''
- try:
- print('Connecting to TRex daemon server @ %s ...' % self.trex_server_path)
- self.server = jsonrpclib.Server(self.trex_server_path, history = self.history)
- self.check_server_connectivity()
- print('Connected TRex server daemon.')
- return True
- except Exception as e:
- print(e)
- return False
+ self.trex_daemon_port = trex_daemon_port
+ self.master_daemon_port = master_daemon_port
+ self.trex_zmq_port = trex_zmq_port
+ self.seq = None
+ self._last_sample = time.time()
+ self.__default_user = get_current_user()
+ self.verbose = verbose
+ self.result_obj = CTRexResult(max_history_size, filtered_latency_amount)
+ self.decoder = JSONDecoder()
+ self.history = jsonrpclib.history.History()
+ self.master_daemon_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = master_daemon_port )
+ self.master_daemon = jsonrpclib.Server(self.master_daemon_path, history = self.history)
+ self.trex_server_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = trex_daemon_port )
+ self.server = jsonrpclib.Server(self.trex_server_path, history = self.history)
+ self.debug_image = debug_image
+ self.trex_args = trex_args
+ self.sample_to_run_finish = self.sample_until_finish # alias for legacy
def add (self, x, y):
@@ -191,7 +164,7 @@ class CTRexClient(object):
self.result_obj.clear_results()
try:
issue_time = time.time()
- retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout)
+ retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, False, self.debug_image, self.trex_args)
except AppError as err:
self._handle_AppError_exception(err.args[0])
except ProtocolError:
@@ -237,7 +210,7 @@ class CTRexClient(object):
"""
try:
user = user or self.__default_user
- retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, True)
+ retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, True, self.debug_image, self.trex_args)
except AppError as err:
self._handle_AppError_exception(err.args[0])
except ProtocolError:
@@ -322,18 +295,28 @@ class CTRexClient(object):
finally:
self.prompt_verbose_data()
- def kill_all_trexes(self):
+ def kill_all_trexes(self, timeout = 15):
"""
Kills running TRex processes (if exists) on the server, not only owned by current daemon.
Raises exception upon error killing.
:return:
- + **True** if any process killed
+ + **True** if processes killed/not running
+ **False** otherwise.
"""
try:
- return self.server.kill_all_trexes()
+ poll_rate = 0.1
+ # try Ctrl+C, usual kill, -9
+ for signal_name in [signal.SIGINT, signal.SIGTERM, signal.SIGKILL]:
+ self.server.kill_all_trexes(signal_name)
+ for i in range(int(timeout / poll_rate)):
+ if not self.get_trex_cmds():
+ return True
+ time.sleep(poll_rate)
+ if self.get_trex_cmds():
+ return False
+ return True
except AppError as err:
self._handle_AppError_exception(err.args[0])
finally:
@@ -555,7 +538,7 @@ class CTRexClient(object):
finally:
self.prompt_verbose_data()
- def sample_until_condition (self, condition_func, time_between_samples = 5):
+ def sample_until_condition (self, condition_func, time_between_samples = 1):
"""
Automatically sets ongoing sampling of TRex data, with sampling rate described by time_between_samples.
@@ -569,7 +552,7 @@ class CTRexClient(object):
time_between_samples : int
determines the time between each sample of the server
- default value : **5**
+ default value : **1**
:return:
the first result object (see :class:`CTRexResult` for further details) of the TRex run on which the condition has been met.
@@ -599,15 +582,15 @@ class CTRexClient(object):
# this could come from provided method 'condition_func'
raise
- def sample_to_run_finish (self, time_between_samples = 5):
+ def sample_until_finish (self, time_between_samples = 1):
"""
- Automatically sets automatically sampling of TRex data with sampling rate described by time_between_samples until TRex run finished.
+ Automatically samples TRex data with sampling rate described by time_between_samples until TRex run finishes.
:parameters:
time_between_samples : int
determines the time between each sample of the server
- default value : **5**
+ default value : **1**
:return:
the latest result object (see :class:`CTRexResult` for further details) with sampled data.
@@ -629,7 +612,7 @@ class CTRexClient(object):
results = self.get_result_obj()
return results
- def sample_x_seconds (self, sample_time, time_between_samples = 5):
+ def sample_x_seconds (self, sample_time, time_between_samples = 1):
"""
Automatically sets ongoing sampling of TRex data for sample_time seconds, with sampling rate described by time_between_samples.
Does not stop the TRex afterwards!
@@ -643,7 +626,7 @@ class CTRexClient(object):
time_between_samples : int
determines the time between each sample of the server
- default value : **5**
+ default value : **1**
:return:
the first result object (see :class:`CTRexResult` for further details) of the TRex run after given sample_time.
@@ -657,12 +640,12 @@ class CTRexClient(object):
"""
# make sure TRex is running. raise exceptions here if any
self.wait_until_kickoff_finish()
- elapsed_time = 0
+ end_time = time.time() + sample_time
while self.is_running():
- if elapsed_time >= sample_time:
+ if time.time() < end_time:
+ time.sleep(time_between_samples)
+ else:
return self.get_result_obj()
- time.sleep(time_between_samples)
- elapsed_time += time_between_samples
raise UserWarning("TRex has stopped at %s seconds (before expected %s seconds)\nTry increasing test duration or decreasing sample_time" % (elapsed_time, sample_time))
def get_result_obj (self, copy_obj = True):
@@ -1291,7 +1274,7 @@ class CTRexResult(object):
.. tip:: | Use '.' to enter one level deeper in dictionary hierarchy.
| Use '[i]' to access the i'th indexed object of an array.
- tree_path_to_key : regex
+ regex : regex
apply a regex to filter results out from a multiple results set.
Filter applies only on keys of dictionary type.
@@ -1319,7 +1302,7 @@ class CTRexResult(object):
.. tip:: | Use '.' to enter one level deeper in dictionary hierarchy.
| Use '[i]' to access the i'th indexed object of an array.
- tree_path_to_key : regex
+ regex : regex
apply a regex to filter results out from a multiple results set.
Filter applies only on keys of dictionary type.
@@ -1372,7 +1355,7 @@ class CTRexResult(object):
if not len(self._history):
return -1
- return len(self.__get_value_by_path(self._history[-1], 'trex-global.data', 'opackets-\d+'))
+ return len(self.get_last_value('trex-global.data', 'opackets-\d+'))
def update_result_data (self, latest_dump):
@@ -1403,14 +1386,21 @@ class CTRexResult(object):
# check for up to 2% change between expected and actual
if (self._current_tx_rate['m_tx_bps'] > 0.98 * self._expected_tx_rate['m_tx_expected_bps']):
self._done_warmup = True
+ latest_dump['warmup_barrier'] = True
# handle latency data
if self.latency_checked:
- latency_per_port = self.get_last_value("trex-latecny-v2.data", "port-")
+ # fix typos, by "pointer"
+ if 'trex-latecny-v2' in latest_dump and 'trex-latency-v2' not in latest_dump:
+ latest_dump['trex-latency-v2'] = latest_dump['trex-latecny-v2']
+ if 'trex-latecny' in latest_dump and 'trex-latency' not in latest_dump:
+ latest_dump['trex-latency'] = latest_dump['trex-latecny']
+
+ latency_per_port = self.get_last_value("trex-latency-v2.data", "port-")
self._max_latency = self.__get_filtered_max_latency(latency_per_port, self.filtered_latency_amount)
- avg_latency = self.get_last_value("trex-latecny.data", "avg-")
+ avg_latency = self.get_last_value("trex-latency.data", "avg-")
self._avg_latency = CTRexResult.__avg_all_and_rename_keys(avg_latency)
- avg_win_latency_list = self.get_value_list("trex-latecny.data", "avg-")
+ avg_win_latency_list = self.get_value_list("trex-latency.data", "avg-")
self._avg_window_latency = CTRexResult.__calc_latency_win_stats(avg_win_latency_list)
tx_pkts = CTRexResult.__get_value_by_path(latest_dump, "trex-global.data.m_total_tx_pkts")
@@ -1447,12 +1437,12 @@ class CTRexResult(object):
for i, p in re.findall(r'(\d+)|([\w|-]+)', tree_path):
dct = dct[p or int(i)]
if regex is not None and isinstance(dct, dict):
- res = {}
- for key,val in dct.items():
- match = re.match(regex, key)
- if match:
- res[key]=val
- return res
+ res = {}
+ for key,val in dct.items():
+ match = re.match(regex, key)
+ if match:
+ res[key]=val
+ return res
else:
return dct
except (KeyError, TypeError):
@@ -1500,26 +1490,70 @@ class CTRexResult(object):
@staticmethod
def __get_filtered_max_latency (src_dict, filtered_latency_amount = 0.001):
result = {}
- for port, data in src_dict.items():
- if not port.startswith('port-'):
- continue
- max_port = 'max-%s' % port[5:]
- res = data['hist']
- if not len(res['histogram']):
- result[max_port] = 0
- continue
- result[max_port] = 5 # if sum below will not get to filtered amount, use this value
- sum_high = 0.0
- for elem in reversed(res['histogram']):
- sum_high += elem['val']
- if sum_high >= filtered_latency_amount * res['cnt']:
- result[max_port] = elem['key'] + int('5' + repr(elem['key'])[2:])
- break
+ if src_dict:
+ for port, data in src_dict.items():
+ if not port.startswith('port-'):
+ continue
+ max_port = 'max-%s' % port[5:]
+ res = data['hist']
+ if not len(res['histogram']):
+ result[max_port] = 0
+ continue
+ result[max_port] = 5 # if sum below will not get to filtered amount, use this value
+ sum_high = 0.0
+ for elem in reversed(res['histogram']):
+ sum_high += elem['val']
+ if sum_high >= filtered_latency_amount * res['cnt']:
+ result[max_port] = elem['key'] + int('5' + repr(elem['key'])[2:])
+ break
return result
+ # history iterator after warmup period
+ def _get_steady_state_history_iterator(self):
+ if not self.is_done_warmup():
+ raise Exception('Warm-up period not finished')
+ for index, res in enumerate(self._history):
+ if 'warmup_barrier' in res:
+ for steady_state_index in range(index, max(index, len(self._history) - 1)):
+ yield self._history[steady_state_index]
+ return
+ for index in range(len(self._history) - 1):
+ yield self._history[index]
+
+
+ def get_avg_steady_state_value(self, tree_path_to_key):
+ '''
+ Gets average value after warmup period.
+ For example: <result object>.get_avg_steady_state_value('trex-global.data.m_tx_bps')
+ Usually more accurate than latest history value.
+
+ :parameters:
+ tree_path_to_key : str
+ defines a path to desired data.
+
+ :return:
+ average value at steady state
+
+ :raises:
+ Exception in case steady state period was not reached or tree_path_to_key was not found in result.
+ '''
+ values_arr = [self.__get_value_by_path(res, tree_path_to_key) for res in self._get_steady_state_history_iterator()]
+ values_arr = list(filter(lambda x: x is not None, values_arr))
+ if not values_arr:
+ raise Exception('All the keys are None, probably wrong tree_path_to_key: %s' % tree_path_to_key)
+ return sum(values_arr) / float(len(values_arr))
if __name__ == "__main__":
- pass
+ c = CTRexClient('127.0.0.1')
+ print('restarting daemon')
+ c.restart_trex_daemon()
+ print('kill any running')
+ c.kill_all_trexes()
+ print('start')
+ c.start_stateless()
+ print('sleep')
+ time.sleep(5)
+ print('done')