From a2a634fc8b5bac450ea37f29dde521b7d9e740c8 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 22 Oct 2015 13:44:25 +0300 Subject: minor updates --- .../trex_control_plane/client/trex_hltapi.py | 6 + .../client/trex_stateless_client.py | 140 +-------------------- .../trex_control_plane/console/trex_console.py | 12 +- 3 files changed, 14 insertions(+), 144 deletions(-) mode change 100644 => 100755 scripts/automation/trex_control_plane/client/trex_hltapi.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_hltapi.py b/scripts/automation/trex_control_plane/client/trex_hltapi.py old mode 100644 new mode 100755 index 46c283f8..b180eb5b --- a/scripts/automation/trex_control_plane/client/trex_hltapi.py +++ b/scripts/automation/trex_control_plane/client/trex_hltapi.py @@ -2,6 +2,7 @@ import trex_root_path from client_utils.packet_builder import CTRexPktBuilder +from trex_stateless_client import CTRexStatelessClient print "done!" @@ -10,6 +11,11 @@ class CTRexHltApi(object): def __init__(self): pass + def connect_status(self): + pass + + + def config_traffic(self): pass diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 334496d1..8956aaf5 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -10,6 +10,7 @@ from client_utils.jsonrpc_client import JsonRpcClient, BatchMessage from client_utils.packet_builder import CTRexPktBuilder import json from common.trex_stats import * +from common.trex_streams import * from collections import namedtuple @@ -352,144 +353,5 @@ class CTRexStatelessClient(object): port=self.port) -class CStream(object): - """docstring for CStream""" - DEFAULTS = {"rx_stats": CRxStats, - "mode": CTxMode, - "isg": 5.0, - "next_stream": -1, - "self_start": True, - "enabled": True} - - def __init__(self, **kwargs): - super(CStream, self).__init__() - for k, v in kwargs.items(): - setattr(self, k, v) - # set default values to unset attributes, according to DEFAULTS dict - set_keys = set(kwargs.keys()) - keys_to_set = [x - for x in self.DEFAULTS - if x not in set_keys] - for key in keys_to_set: - default = self.DEFAULTS.get(key) - if type(default) == type: - setattr(self, key, default()) - else: - setattr(self, key, default) - - @property - def packet(self): - return self._packet - - @packet.setter - def packet(self, packet_obj): - assert isinstance(packet_obj, CTRexPktBuilder) - self._packet = packet_obj - - @property - def enabled(self): - return self._enabled - - @enabled.setter - def enabled(self, bool_value): - self._enabled = bool(bool_value) - - @property - def self_start(self): - return self._self_start - - @self_start.setter - def self_start(self, bool_value): - self._self_start = bool(bool_value) - - @property - def next_stream(self): - return self._next_stream - - @next_stream.setter - def next_stream(self, value): - self._next_stream = int(value) - - def dump(self): - pass - return {"enabled": self.enabled, - "self_start": self.self_start, - "isg": self.isg, - "next_stream": self.next_stream, - "packet": self.packet.dump_pkt(), - "mode": self.mode.dump(), - "vm": self.packet.get_vm_data(), - "rx_stats": self.rx_stats.dump()} - -class CRxStats(object): - - def __init__(self, enabled=False, seq_enabled=False, latency_enabled=False): - self._rx_dict = {"enabled": enabled, - "seq_enabled": seq_enabled, - "latency_enabled": latency_enabled} - - @property - def enabled(self): - return self._rx_dict.get("enabled") - - @enabled.setter - def enabled(self, bool_value): - self._rx_dict['enabled'] = bool(bool_value) - - @property - def seq_enabled(self): - return self._rx_dict.get("seq_enabled") - - @seq_enabled.setter - def seq_enabled(self, bool_value): - self._rx_dict['seq_enabled'] = bool(bool_value) - - @property - def latency_enabled(self): - return self._rx_dict.get("latency_enabled") - - @latency_enabled.setter - def latency_enabled(self, bool_value): - self._rx_dict['latency_enabled'] = bool(bool_value) - - def dump(self): - return {k: v - for k, v in self._rx_dict.items() - if v - } - - -class CTxMode(object): - """docstring for CTxMode""" - def __init__(self, tx_mode, pps): - super(CTxMode, self).__init__() - if tx_mode not in ["continuous", "single_burst", "multi_burst"]: - raise ValueError("Unknown TX mode ('{0}')has been initialized.".format(tx_mode)) - self._tx_mode = tx_mode - self._fields = {'pps': float(pps)} - if tx_mode == "single_burst": - self._fields['total_pkts'] = 0 - elif tx_mode == "multi_burst": - self._fields['pkts_per_burst'] = 0 - self._fields['ibg'] = 0.0 - self._fields['count'] = 0 - else: - pass - - def set_tx_mode_attr(self, attr, val): - if attr in self._fields: - self._fields[attr] = type(self._fields.get(attr))(val) - else: - raise ValueError("The provided attribute ('{0}') is not a legal attribute in selected TX mode ('{1}')". - format(attr, self._tx_mode)) - - def dump(self): - dump = {"type": self._tx_mode} - dump.update({k: v - for k, v in self._fields.items() - }) - return dump - - if __name__ == "__main__": pass diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index a9ac040b..0f5c30af 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -17,6 +17,8 @@ See the License for the specific language governing permissions and limitations under the License. """ +__version__ = "1.0" + import cmd import json import ast @@ -38,7 +40,7 @@ LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) # -def readch (choices = []): +def readch(choices=[]): fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) @@ -119,7 +121,7 @@ class AddStreamMenu(CmdMenu): self.add_menu('Please select ISG', ['d', 'e', 'f']) # main console object -class TrexConsole(cmd.Cmd): +class TRexConsole(cmd.Cmd): """Trex Console""" def __init__(self, rpc_client): @@ -129,7 +131,7 @@ class TrexConsole(cmd.Cmd): self.do_connect("") - self.intro = "\n-=TRex Console V1.0=-\n" + self.intro = "\n-=TRex Console v{ver}=-\n".format(ver=__version__) self.intro += "\nType 'help' or '?' for supported actions\n" self.verbose = False @@ -473,7 +475,7 @@ class TrexConsole(cmd.Cmd): def complete_load_stream_list(self, text, line, begidx, endidx): arg_num = len(line.split()) - 1 if arg_num == 2: - return TrexConsole.tree_autocomplete(line.split()[-1]) + return TRexConsole.tree_autocomplete(line.split()[-1]) else: return [text] @@ -581,7 +583,7 @@ def main (): # console try: - console = TrexConsole(rpc_client) + console = TRexConsole(rpc_client) console.cmdloop() except KeyboardInterrupt as e: print "\n\n*** Caught Ctrl + C... Exiting...\n\n" -- cgit From ebb0b48faca96bad7bfe8da0bf80df7c7c80350d Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 25 Oct 2015 11:33:48 +0200 Subject: HLTAPI progress... --- .../trex_control_plane/client/trex_hltapi.py | 30 ++++++++++++++++++++-- .../client/trex_stateless_client.py | 5 ++++ .../client_utils/jsonrpc_client.py | 4 +-- .../trex_control_plane/console/trex_console.py | 3 ++- 4 files changed, 37 insertions(+), 5 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_hltapi.py b/scripts/automation/trex_control_plane/client/trex_hltapi.py index b180eb5b..7453d8ec 100755 --- a/scripts/automation/trex_control_plane/client/trex_hltapi.py +++ b/scripts/automation/trex_control_plane/client/trex_hltapi.py @@ -11,14 +11,40 @@ class CTRexHltApi(object): def __init__(self): pass - def connect_status(self): + def connect(self, device, port_list, username, reset=False, break_locks=False): pass + def interface_config(self, port_handle, mode="config"): + pass + + def get_status(self): + pass + + def get_port_handler(self): + pass + + def traffic_config(self, mode, port_handle, + mac_src, mac_dst, + l3_protocol, ip_src_addr, ip_dst_addr, l3_length, + transmit_mode, rate_pps): + pass + def traffic_control(self, action, port_handle): + pass - def config_traffic(self): + def traffic_stats(self, port_handle, mode): pass + def get_aggregate_port_stats(self, port_handle): + return self.traffic_stats(port_handle, mode="aggregate") + + def get_stream_stats(self, port_handle): + return self.traffic_stats(port_handle, mode="stream") + + + + + if __name__ == "__main__": pass diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 8956aaf5..7d287a5e 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -63,6 +63,9 @@ class CTRexStatelessClient(object): return self._system_info # ----- user-access methods ----- # + def connect(self): + self.tx_link.connect() + def ping(self): return self.transmit("ping") @@ -323,6 +326,8 @@ class CTRexStatelessClient(object): self.server = server self.port = port self.rpc_link = JsonRpcClient(self.server, self.port) + + def connect(self): if not self.virtual: self.rpc_link.connect() diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index ed14e6f8..1c5dbe7e 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -182,7 +182,7 @@ class JsonRpcClient(object): # if no error there should be a result if ("result" not in response_json): - return False, "Malfromed Response ({0})".format(str(response)) + return False, "Malformed Response ({0})".format(str(response)) return True, response_json["result"] @@ -200,7 +200,7 @@ class JsonRpcClient(object): else: return False, "Not connected to server" - def connect(self, server = None, port = None): + def connect(self, server=None, port=None): if self.connected: self.disconnect() diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 0f5c30af..4f9743f4 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -17,7 +17,6 @@ See the License for the specific language governing permissions and limitations under the License. """ -__version__ = "1.0" import cmd import json @@ -36,6 +35,8 @@ from client_utils.jsonrpc_client import TrexStatelessClient import trex_status from collections import namedtuple +__version__ = "1.0" + LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) # -- cgit From 7c35235f67b009cb2a9a605c3c89ea39d6f10871 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 25 Oct 2015 06:17:33 +0200 Subject: refactor tx_link -> comm_link --- .../trex_control_plane/client/trex_stateless_client.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 7d287a5e..8856a793 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -21,7 +21,7 @@ class CTRexStatelessClient(object): def __init__(self, username, server="localhost", port=5050, virtual=False): super(CTRexStatelessClient, self).__init__() self.user = username - self.tx_link = CTRexStatelessClient.CTxLink(server, port, virtual) + self.comm_link = CTRexStatelessClient.CCommLink(server, port, virtual) self._conn_handler = {} self._active_ports = set() self._stats = CTRexStatsManager("port", "stream") @@ -64,7 +64,7 @@ class CTRexStatelessClient(object): # ----- user-access methods ----- # def connect(self): - self.tx_link.connect() + self.comm_link.connect() def ping(self): return self.transmit("ping") @@ -238,10 +238,10 @@ class CTRexStatelessClient(object): # ----- internal methods ----- # def transmit(self, method_name, params={}): - return self.tx_link.transmit(method_name, params) + return self.comm_link.transmit(method_name, params) def transmit_batch(self, batch_list): - return self.tx_link.transmit_batch(batch_list) + return self.comm_link.transmit_batch(batch_list) @staticmethod def _object_decoder(obj_type, obj_data): @@ -318,10 +318,10 @@ class CTRexStatelessClient(object): # ------ private classes ------ # - class CTxLink(object): + class CCommLink(object): """describes the connectivity of the stateless client method""" def __init__(self, server="localhost", port=5050, virtual=False): - super(CTRexStatelessClient.CTxLink, self).__init__() + super(CTRexStatelessClient.CCommLink, self).__init__() self.virtual = virtual self.server = server self.port = port -- cgit From de90762476583bed8c3a2c1e15158b85e4231ad0 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Mon, 26 Oct 2015 01:19:06 +0200 Subject: fixed streamlist loading --- scripts/automation/trex_control_plane/common/trex_streams.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py index 783f2769..efa8e5cc 100755 --- a/scripts/automation/trex_control_plane/common/trex_streams.py +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -48,6 +48,7 @@ class CStreamList(object): self.streams_list.clear() streams_data = load_yaml_to_obj(file_path) assert isinstance(streams_data, list) + new_streams_data = [] for stream in streams_data: stream_name = stream.get("name") raw_stream = stream.get("stream") @@ -58,10 +59,11 @@ class CStreamList(object): new_stream_data = self.yaml_loader.validate_yaml(raw_stream, "stream", multiplier= multiplier) + new_streams_data.append(new_stream_data) new_stream_obj = CStream() new_stream_obj.load_data(**new_stream_data) self.append_stream(stream_name, new_stream_obj) - return new_stream_data + return new_streams_data def compile_streams(self): # first, assign an id to each stream @@ -156,7 +158,6 @@ class CStream(object): """docstring for CStream""" FIELDS = ["enabled", "self_start", "next_stream_id", "isg", "mode", "rx_stats", "packet", "vm"] - # COMPILE_FIELDS = ["enabled", "self_start", "next_stream_id", "isg", "mode", "rx_stats", "packet", "vm"] def __init__(self): self.is_loaded = False -- cgit From b44239e4c6019f10fa7cf4fe0fef8c3726435033 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Mon, 26 Oct 2015 09:41:51 +0200 Subject: add disconnect functionality --- .../automation/trex_control_plane/client/trex_stateless_client.py | 3 +++ scripts/automation/trex_control_plane/common/trex_streams.py | 7 +------ 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 8856a793..b976db66 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -66,6 +66,9 @@ class CTRexStatelessClient(object): def connect(self): self.comm_link.connect() + def disconnect(self): + self.comm_link.disconnect() + def ping(self): return self.transmit("ping") diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py index efa8e5cc..750da198 100755 --- a/scripts/automation/trex_control_plane/common/trex_streams.py +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -227,8 +227,7 @@ class CStream(object): return - def dump(self, compilation=False): - # fields = CStream.COMPILE_FIELDS if compilation else CStream.FIELDS + def dump(self): if self.is_loaded: dump = {} for key in CStream.FIELDS: @@ -240,10 +239,6 @@ class CStream(object): else: raise RuntimeError("CStream object isn't loaded with data. Use 'load_data' method.") - def dump_compiled(self): - return self.dump(compilation=True) - - if __name__ == "__main__": pass -- cgit From a39e4416cd78fc3b147695465c4de1c896b3face Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 27 Oct 2015 10:00:18 +0200 Subject: more hltapi progress connect working --- .../trex_control_plane/client/trex_hltapi.py | 72 +++++++- .../client/trex_stateless_client.py | 185 ++++++++++++++++----- .../client_utils/jsonrpc_client.py | 27 +-- 3 files changed, 227 insertions(+), 57 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_hltapi.py b/scripts/automation/trex_control_plane/client/trex_hltapi.py index 7453d8ec..6194d376 100755 --- a/scripts/automation/trex_control_plane/client/trex_hltapi.py +++ b/scripts/automation/trex_control_plane/client/trex_hltapi.py @@ -4,14 +4,54 @@ import trex_root_path from client_utils.packet_builder import CTRexPktBuilder from trex_stateless_client import CTRexStatelessClient -print "done!" class CTRexHltApi(object): def __init__(self): + self.trex_client = None + self.connected = False + + pass + + # ----- session functions ----- # + + def connect(self, device, port_list, username, port=5050, reset=False, break_locks=False): + ret_dict = {"status": 0} + self.trex_client = CTRexStatelessClient(username, device, port) + res_ok, msg = self.trex_client.connect() + if not res_ok: + self.trex_client = None + ret_dict.update({"log": msg}) + return ret_dict + # arrived here, connection successfully created with server + # next, try acquiring ports of TRex + port_list = self.parse_port_list(port_list) + response = self.trex_client.acquire(port_list, force=break_locks) + res_ok, log = CTRexHltApi.process_response(port_list, response) + if not res_ok: + self.trex_client.disconnect() + self.trex_client = None + ret_dict.update({"log": log}) + # TODO: should revert taken ports?! + return ret_dict + # arrived here, all desired ports were successfully acquired + print log + if reset: + # remove all port traffic configuration from TRex + response = self.trex_client.remove_all_streams(port_list) + res_ok, log = CTRexHltApi.process_response(port_list, response) + if not res_ok: + self.trex_client.disconnect() + self.trex_client = None + ret_dict.update({"log": log}) + return ret_dict + print log + ret_dict.update({"status": 1}) + self.trex_client.disconnect() + pass - def connect(self, device, port_list, username, reset=False, break_locks=False): + def cleanup_session(self, port_list, maintain_lock=False): pass def interface_config(self, port_handle, mode="config"): @@ -41,8 +81,32 @@ class CTRexHltApi(object): def get_stream_stats(self, port_handle): return self.traffic_stats(port_handle, mode="stream") - - + # ----- internal functions ----- # + @staticmethod + def process_response(port_list, response): + if isinstance(port_list, list): + res_ok, response = response + log = CTRexHltApi.join_batch_response(response) + else: + res_ok = response.success + log = str(response) + return res_ok, log + + @staticmethod + def parse_port_list(port_list): + if isinstance(port_list, str): + return [int(port) + for port in port_list.split()] + elif isinstance(port_list, list): + return [int(port) + for port in port_list] + else: + return port_list + + @staticmethod + def join_batch_response(responses): + return "\n".join([str(response) + for response in responses]) if __name__ == "__main__": diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index b976db66..97d3ec0a 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -13,10 +13,19 @@ from common.trex_stats import * from common.trex_streams import * from collections import namedtuple +RpcCmdData = namedtuple('RpcCmdData', ['method', 'params']) + +class RpcResponseStatus(namedtuple('RpcResponseStatus', ['success', 'id', 'msg'])): + __slots__ = () + def __str__(self): + return "{id:^3} - {msg} ({stat})".format(id=self.id, + msg=self.msg, + stat="success" if self.success else "fail") + +# RpcResponseStatus = namedtuple('RpcResponseStatus', ['success', 'id', 'msg']) class CTRexStatelessClient(object): """docstring for CTRexStatelessClient""" - RpcCmdData = namedtuple('RpcCmdData', ['method', 'params']) def __init__(self, username, server="localhost", port=5050, virtual=False): super(CTRexStatelessClient, self).__init__() @@ -26,48 +35,69 @@ class CTRexStatelessClient(object): self._active_ports = set() self._stats = CTRexStatsManager("port", "stream") self._system_info = None + self._server_version = None + self.__err_log = None # ----- decorator methods ----- # def force_status(owned=True, active_and_owned=False): def wrapper(func): def wrapper_f(self, *args, **kwargs): port_ids = kwargs.get("port_id") + if not port_ids: + port_ids = args[0] if isinstance(port_ids, int): # make sure port_ids is a list port_ids = [port_ids] bad_ids = set() for port_id in port_ids: - port_owned = self._conn_handler.get(kwargs.get(port_id)) + port_owned = self._conn_handler.get(port_id) if owned and not port_owned: - bad_ids.add(port_ids) + bad_ids.add(port_id) elif active_and_owned: # stronger condition than just owned, hence gets precedence if port_owned and port_id in self._active_ports: continue else: - bad_ids.add(port_ids) + bad_ids.add(port_id) else: continue if bad_ids: # Some port IDs are not according to desires status raise RuntimeError("The requested method ('{0}') cannot be invoked since port IDs {1} are not" - "at allowed stated".format(func.__name__)) + "at allowed stated".format(func.__name__, list(bad_ids))) else: - func(self, *args, **kwargs) + return func(self, *args, **kwargs) return wrapper_f return wrapper @property def system_info(self): if not self._system_info: - self._system_info = self.get_system_info() - return self._system_info + rc, info = self.get_system_info() + if rc: + self._system_info = info + else: + self.__err_log = info + return self._system_info if self._system_info else "Unknown" + + @property + def server_version(self): + if not self._server_version: + rc, ver_info = self.get_version() + if rc: + self._server_version = ver_info + else: + self.__err_log = ver_info + return self._server_version if self._server_version else "Unknown" # ----- user-access methods ----- # def connect(self): - self.comm_link.connect() + rc, err = self.comm_link.connect() + if not rc: + return rc, err + return self._init_sync() def disconnect(self): - self.comm_link.disconnect() + return self.comm_link.disconnect() def ping(self): return self.transmit("ping") @@ -90,18 +120,19 @@ class CTRexStatelessClient(object): if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications - commands = [self.RpcCmdData("acquire", {"port_id": p_id, "user": self.user, "force": force}) + commands = [RpcCmdData("acquire", {"port_id": p_id, "user": self.user, "force": force}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) if rc: - self._process_batch_result(commands, resp_list, self._handle_acquire_response) + return self._process_batch_result(commands, resp_list, self._handle_acquire_response) else: params = {"port_id": port_id, "user": self.user, "force": force} - command = self.RpcCmdData("acquire", params) - self._handle_acquire_response(command, self.transmit(command.method, command.params)) - return self._conn_handler.get(port_id) + command = RpcCmdData("acquire", params) + return self._handle_acquire_response(command, + self.transmit(command.method, command.params), + self.default_success_test) @force_status(owned=True) def release(self, port_id=None): @@ -110,7 +141,7 @@ class CTRexStatelessClient(object): if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications - commands = [self.RpcCmdData("release", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + commands = [RpcCmdData("release", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) if rc: @@ -119,7 +150,7 @@ class CTRexStatelessClient(object): self._conn_handler.pop(port_id) params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} - command = self.RpcCmdData("release", params) + command = RpcCmdData("release", params) self._handle_release_response(command, self.transmit(command.method, command.params)) return @@ -143,6 +174,28 @@ class CTRexStatelessClient(object): "stream_id": stream_id} return self.transmit("remove_stream", params) + @force_status(owned=True) + def remove_all_streams(self, port_id=None): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [RpcCmdData("remove_all_streams", {"port_id": p_id, "handler": self._conn_handler.get(p_id)}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + if rc: + return self._process_batch_result(commands, resp_list, self._handle_remove_streams_response, + success_test=self.ack_success_test) + else: + params = {"port_id": port_id, + "handler": self._conn_handler.get(port_id)} + command = RpcCmdData("remove_all_streams", params) + return self._handle_remove_streams_response(command, + self.transmit(command.method, command.params), + self.ack_success_test) + pass + @force_status(owned=True, active_and_owned=True) def get_stream_id_list(self, port_id=None): if not self._is_ports_valid(port_id): @@ -167,7 +220,7 @@ class CTRexStatelessClient(object): if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications - commands = [self.RpcCmdData("start_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + commands = [RpcCmdData("start_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) if rc: @@ -175,7 +228,7 @@ class CTRexStatelessClient(object): else: params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} - command = self.RpcCmdData("start_traffic", params) + command = RpcCmdData("start_traffic", params) self._handle_start_traffic_response(command, self.transmit(command.method, command.params)) return @@ -186,7 +239,7 @@ class CTRexStatelessClient(object): if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications - commands = [self.RpcCmdData("stop_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + commands = [RpcCmdData("stop_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) if rc: @@ -194,12 +247,12 @@ class CTRexStatelessClient(object): else: params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} - command = self.RpcCmdData("stop_traffic", params) + command = RpcCmdData("stop_traffic", params) self._handle_start_traffic_response(command, self.transmit(command.method, command.params)) return def get_global_stats(self): - command = self.RpcCmdData("get_global_stats", {}) + command = RpcCmdData("get_global_stats", {}) return self._handle_get_global_stats_response(command, self.transmit(command.method, command.params)) # return self.transmit("get_global_stats") @@ -210,7 +263,7 @@ class CTRexStatelessClient(object): if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications - commands = [self.RpcCmdData("get_port_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + commands = [RpcCmdData("get_port_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) if rc: @@ -218,7 +271,7 @@ class CTRexStatelessClient(object): else: params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} - command = self.RpcCmdData("get_port_stats", params) + command = RpcCmdData("get_port_stats", params) return self._handle_get_port_stats_response(command, self.transmit(command.method, command.params)) @force_status(owned=True, active_and_owned=True) @@ -228,7 +281,7 @@ class CTRexStatelessClient(object): if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications - commands = [self.RpcCmdData("get_stream_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + commands = [RpcCmdData("get_stream_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) if rc: @@ -236,10 +289,18 @@ class CTRexStatelessClient(object): else: params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} - command = self.RpcCmdData("get_stream_stats", params) + command = RpcCmdData("get_stream_stats", params) return self._handle_get_stream_stats_response(command, self.transmit(command.method, command.params)) # ----- internal methods ----- # + def _init_sync(self): + # get server version and system info + err = False + if self.server_version == "Unknown" or self.system_info == "Unknown": + self.disconnect() + return False, self.__err_log + return True, "" + def transmit(self, method_name, params={}): return self.comm_link.transmit(method_name, params) @@ -265,16 +326,46 @@ class CTRexStatelessClient(object): else: return False + @staticmethod + def ack_success_test(result_obj): + if result_obj.success and result_obj.data == "ACK": + return True + else: + return False + + # ----- handler internal methods ----- # - def _handle_acquire_response(self, request, response): - if response.success: - self._conn_handler[request.get("port_id")] = response.data + def _handle_general_response(self, request, response, msg, success_test=None): + port_id = request.params.get("port_id") + if not success_test: + success_test = self.default_success_test + if success_test(response): + self._conn_handler[port_id] = response.data + return RpcResponseStatus(True, port_id, msg) + else: + return RpcResponseStatus(False, port_id, response.data) + - def _handle_release_response(self, request, response): + def _handle_acquire_response(self, request, response, success_test): + port_id = request.params.get("port_id") + if success_test(response): + self._conn_handler[port_id] = response.data + return RpcResponseStatus(True, port_id, "Acquired") + else: + return RpcResponseStatus(False, port_id, response.data) + + def _handle_remove_streams_response(self, request, response, success_test): + port_id = request.params.get("port_id") + if success_test(response): + return RpcResponseStatus(True, port_id, "Removed") + else: + return RpcResponseStatus(False, port_id, response.data) + + def _handle_release_response(self, request, response, success_test): if response.success: del self._conn_handler[request.get("port_id")] - def _handle_start_traffic_response(self, request, response): + def _handle_start_traffic_response(self, request, response, success_test): if response.success: self._active_ports.add(request.get("port_id")) @@ -282,19 +373,19 @@ class CTRexStatelessClient(object): if response.success: self._active_ports.remove(request.get("port_id")) - def _handle_get_global_stats_response(self, request, response): + def _handle_get_global_stats_response(self, request, response, success_test): if response.success: return CGlobalStats(**response.success) else: return False - def _handle_get_port_stats_response(self, request, response): + def _handle_get_port_stats_response(self, request, response, success_test): if response.success: return CPortStats(**response.success) else: return False - def _handle_get_stream_stats_response(self, request, response): + def _handle_get_stream_stats_response(self, request, response, success_test): if response.success: return CStreamStats(**response.success) else: @@ -311,13 +402,21 @@ class CTRexStatelessClient(object): return False def _process_batch_result(self, req_list, resp_list, handler_func=None, success_test=default_success_test): + res_ok = True + responses = [] + if isinstance(success_test, staticmethod): + success_test = success_test.__func__ for i, response in enumerate(resp_list): - # testing each result with success test so that a conclusion report could be deployed in future. - if success_test(response): - # run handler method with its params - handler_func(req_list[i], response) - else: - continue # TODO: mark in this case somehow the bad result + # run handler method with its params + processed_response = handler_func(req_list[i], response, success_test) + responses.append(processed_response) + if not processed_response.success: + res_ok = False + # else: + # res_ok = False # TODO: mark in this case somehow the bad result + # print res_ok + # print responses + return res_ok, responses # ------ private classes ------ # @@ -332,7 +431,11 @@ class CTRexStatelessClient(object): def connect(self): if not self.virtual: - self.rpc_link.connect() + return self.rpc_link.connect() + + def disconnect(self): + if not self.virtual: + self.rpc_link.disconnect() def transmit(self, method_name, params={}): if self.virtual: diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index 1c5dbe7e..fe94e5ef 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -6,6 +6,9 @@ import json import general_utils import re from time import sleep +from collections import namedtuple + +CmdResponse = namedtuple('CmdResponse', ['success', 'data']) class bcolors: BLUE = '\033[94m' @@ -23,12 +26,12 @@ class BatchMessage(object): self.rpc_client = rpc_client self.batch_list = [] - def add (self, method_name, params = {}): + def add (self, method_name, params={}): id, msg = self.rpc_client.create_jsonrpc_v2(method_name, params, encode = False) self.batch_list.append(msg) - def invoke (self, block = False): + def invoke(self, block = False): if not self.rpc_client.connected: return False, "Not connected to server" @@ -36,9 +39,9 @@ class BatchMessage(object): rc, resp_list = self.rpc_client.send_raw_msg(msg, block = False) if len(self.batch_list) == 1: - return True, [(rc, resp_list)] + return CmdResponse(True, [CmdResponse(rc, resp_list)]) else: - return rc, resp_list + return CmdResponse(rc, resp_list) # JSON RPC v2.0 client @@ -47,7 +50,7 @@ class JsonRpcClient(object): def __init__ (self, default_server, default_port): self.verbose = False self.connected = False - + # default values self.port = default_port self.server = default_server @@ -73,7 +76,7 @@ class JsonRpcClient(object): # float pretty_str = re.sub(r'([ ]*:[ ]+)(\-?[1-9][0-9]*\.[0-9]+)',r'\1{0}\2{1}'.format(bcolors.MAGENTA, bcolors.ENDC), pretty_str) # strings - + pretty_str = re.sub(r'([ ]*:[ ]+)("[^"]*")',r'\1{0}\2{1}'.format(bcolors.RED, bcolors.ENDC), pretty_str) pretty_str = re.sub(r"('[^']*')", r'{0}\1{1}'.format(bcolors.MAGENTA, bcolors.RED), pretty_str) except : @@ -115,7 +118,7 @@ class JsonRpcClient(object): return self.send_raw_msg(msg, block) - + # low level send of string message def send_raw_msg (self, msg, block = False): self.verbose_msg("Sending Request To Server:\n\n" + self.pretty_json(msg) + "\n") @@ -127,7 +130,7 @@ class JsonRpcClient(object): self.socket.send(msg, flags = zmq.NOBLOCK) except zmq.error.ZMQError as e: self.disconnect() - return False, "Failed To Get Send Message" + return CmdResponse(False, "Failed To Get Send Message") got_response = False @@ -145,7 +148,7 @@ class JsonRpcClient(object): if not got_response: self.disconnect() - return False, "Failed To Get Server Response" + return CmdResponse(False, "Failed To Get Server Response") self.verbose_msg("Server Response:\n\n" + self.pretty_json(response) + "\n") @@ -159,13 +162,13 @@ class JsonRpcClient(object): for single_response in response_json: rc, msg = self.process_single_response(single_response) - rc_list.append( (rc, msg) ) + rc_list.append( CmdResponse(rc, msg) ) - return True, rc_list + return CmdResponse(True, rc_list) else: rc, msg = self.process_single_response(response_json) - return rc, msg + return CmdResponse(rc, msg) def process_single_response (self, response_json): -- cgit From d2453a6b449762b478975418289dfcfc51dcc395 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 27 Oct 2015 15:59:35 +0200 Subject: more HLTAPI --- scripts/automation/trex_control_plane/examples/stateless_example.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 scripts/automation/trex_control_plane/examples/stateless_example.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/examples/stateless_example.py b/scripts/automation/trex_control_plane/examples/stateless_example.py new file mode 100644 index 00000000..f97bce4c --- /dev/null +++ b/scripts/automation/trex_control_plane/examples/stateless_example.py @@ -0,0 +1 @@ +__author__ = 'danklei' -- cgit From 3bebf60874ec07d157944088ee3cfd5909eccc0f Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 27 Oct 2015 16:02:29 +0200 Subject: More HLTAPI --- .../trex_control_plane/client/trex_hltapi.py | 67 +++++++++++++++++----- .../client/trex_stateless_client.py | 19 ++++-- 2 files changed, 68 insertions(+), 18 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_hltapi.py b/scripts/automation/trex_control_plane/client/trex_hltapi.py index 6194d376..b237071f 100755 --- a/scripts/automation/trex_control_plane/client/trex_hltapi.py +++ b/scripts/automation/trex_control_plane/client/trex_hltapi.py @@ -3,6 +3,7 @@ import trex_root_path from client_utils.packet_builder import CTRexPktBuilder from trex_stateless_client import CTRexStatelessClient +import dpkt class CTRexHltApi(object): @@ -46,28 +47,68 @@ class CTRexHltApi(object): ret_dict.update({"log": log}) return ret_dict print log - ret_dict.update({"status": 1}) - self.trex_client.disconnect() + port_handle = {device: {port: port # since only supporting single TRex at the moment, 1:1 map + for port in port_list} + } - pass + ret_dict.update({"status": 1, + "log": None, + "port_handle": port_handle, + "offline": 0}) # ports are online + return ret_dict def cleanup_session(self, port_list, maintain_lock=False): - pass + ret_dict = {"status": 0} + if not maintain_lock: + # release taken ports + if port_list == "all": + port_list = self.trex_client.get_acquired_ports() + else: + port_list = self.parse_port_list(port_list) + response = self.trex_client.release(port_list) + res_ok, log = CTRexHltApi.process_response(port_list, response) + print log + if not res_ok: + ret_dict.update({"log": log}) + return ret_dict + ret_dict.update({"status": 1, + "log": None}) + self.trex_client.disconnect() + self.trex_client = None + return ret_dict def interface_config(self, port_handle, mode="config"): - pass - - def get_status(self): - pass - - def get_port_handler(self): - pass + allowed_modes = ["config", "modify", "destroy"] + if mode not in allowed_modes: + raise ValueError("mode must be one of the following values: {modes}".format(modes=allowed_modes)) + # pass this function for now... + return {"status": 1, "log": None} + # ----- traffic functions ----- # def traffic_config(self, mode, port_handle, - mac_src, mac_dst, + l2_encap, mac_src, mac_dst, l3_protocol, ip_src_addr, ip_dst_addr, l3_length, transmit_mode, rate_pps): - pass + allowed_modes = ["create", "modify", "remove", "enable", "disable", "reset"] + if mode not in allowed_modes: + raise ValueError("mode must be one of the following values: {modes}".format(modes=allowed_modes)) + if mode == "create": + # create a new stream with desired attributes + pkt_bld = CTRexPktBuilder() + if l2_encap == "ethernet_ii": + pkt_bld.add_pkt_layer("l2", dpkt.ethernet.Ethernet()) + # set Ethernet layer attributes + pkt_bld.set_eth_layer_addr("l2", "src", mac_src) + pkt_bld.set_eth_layer_addr("l2", "dst", mac_dst) + else: + raise NotImplementedError("l2_encap does not support the desired encapsulation '{0}'".format(l2_encap)) + # set l3 attrubutes + if l3_protocol == "ipv4": + + + pass + else: + raise NotImplementedError("mode '{0}' is not supported yet on TRex".format(mode)) def traffic_control(self, action, port_handle): pass diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 97d3ec0a..3079ee5e 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -114,6 +114,9 @@ class CTRexStatelessClient(object): def get_port_count(self): return self.system_info.get("port_count") + def get_acquired_ports(self): + return self._conn_handler.keys() + def acquire(self, port_id, force=False): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") @@ -145,14 +148,16 @@ class CTRexStatelessClient(object): for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) if rc: - self._process_batch_result(commands, resp_list, self._handle_release_response) + return self._process_batch_result(commands, resp_list, self._handle_release_response, + success_test=self.ack_success_test) else: self._conn_handler.pop(port_id) params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} command = RpcCmdData("release", params) - self._handle_release_response(command, self.transmit(command.method, command.params)) - return + return self._handle_release_response(command, + self.transmit(command.method, command.params), + self.ack_success_test) @force_status(owned=True) def add_stream(self, stream_id, stream_obj, port_id=None): @@ -362,8 +367,12 @@ class CTRexStatelessClient(object): return RpcResponseStatus(False, port_id, response.data) def _handle_release_response(self, request, response, success_test): - if response.success: - del self._conn_handler[request.get("port_id")] + port_id = request.params.get("port_id") + if success_test(response): + del self._conn_handler[port_id] + return RpcResponseStatus(True, port_id, "Released") + else: + return RpcResponseStatus(False, port_id, response.data) def _handle_start_traffic_response(self, request, response, success_test): if response.success: -- cgit From fe6e03366eae72376f1201ed68744cb1206773de Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Wed, 28 Oct 2015 07:36:08 +0200 Subject: more HLTAPI 2 --- .../trex_control_plane/client/trex_hltapi.py | 64 ++++++++++++++++------ 1 file changed, 48 insertions(+), 16 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_hltapi.py b/scripts/automation/trex_control_plane/client/trex_hltapi.py index b237071f..cc7005e8 100755 --- a/scripts/automation/trex_control_plane/client/trex_hltapi.py +++ b/scripts/automation/trex_control_plane/client/trex_hltapi.py @@ -78,9 +78,9 @@ class CTRexHltApi(object): return ret_dict def interface_config(self, port_handle, mode="config"): - allowed_modes = ["config", "modify", "destroy"] - if mode not in allowed_modes: - raise ValueError("mode must be one of the following values: {modes}".format(modes=allowed_modes)) + ALLOWED_MODES = ["config", "modify", "destroy"] + if mode not in ALLOWED_MODES: + raise ValueError("mode must be one of the following values: {modes}".format(modes=ALLOWED_MODES)) # pass this function for now... return {"status": 1, "log": None} @@ -89,21 +89,15 @@ class CTRexHltApi(object): l2_encap, mac_src, mac_dst, l3_protocol, ip_src_addr, ip_dst_addr, l3_length, transmit_mode, rate_pps): - allowed_modes = ["create", "modify", "remove", "enable", "disable", "reset"] - if mode not in allowed_modes: - raise ValueError("mode must be one of the following values: {modes}".format(modes=allowed_modes)) + ALLOWED_MODES = ["create", "modify", "remove", "enable", "disable", "reset"] + if mode not in ALLOWED_MODES: + raise ValueError("mode must be one of the following values: {modes}".format(modes=ALLOWED_MODES)) if mode == "create": # create a new stream with desired attributes - pkt_bld = CTRexPktBuilder() - if l2_encap == "ethernet_ii": - pkt_bld.add_pkt_layer("l2", dpkt.ethernet.Ethernet()) - # set Ethernet layer attributes - pkt_bld.set_eth_layer_addr("l2", "src", mac_src) - pkt_bld.set_eth_layer_addr("l2", "dst", mac_dst) - else: - raise NotImplementedError("l2_encap does not support the desired encapsulation '{0}'".format(l2_encap)) - # set l3 attrubutes - if l3_protocol == "ipv4": + stream = CTRexHltApi.generate_stream(l2_encap, mac_src, mac_dst, + l3_protocol, ip_src_addr, ip_dst_addr, l3_length, + transmit_mode, rate_pps) + pass @@ -149,6 +143,44 @@ class CTRexHltApi(object): return "\n".join([str(response) for response in responses]) + @staticmethod + def generate_stream(l2_encap, mac_src, mac_dst, + l3_protocol, ip_src_addr, ip_dst_addr, l3_length, + transmit_mode, rate_pps): + ALLOWED_L3_PROTOCOL = {"ipv4": dpkt.ethernet.ETH_TYPE_IP, + "ipv6": dpkt.ethernet.ETH_TYPE_IP6, + "arp": dpkt.ethernet.ETH_TYPE_ARP} + ALLOWED_L4_PROTOCOL = {"tcp": dpkt.ip.IP_PROTO_TCP, + "udp": dpkt.ip.IP_PROTO_UDP, + "icmp": dpkt.ip.IP_PROTO_ICMP, + "icmpv6": dpkt.ip.IP_PROTO_ICMP6, + "igmp": dpkt.ip.IP_PROTO_IGMP, + "rtp": dpkt.ip.IP_PROTO_IRTP, + "isis": dpkt.ip.IP_PROTO_ISIS, + "ospf": dpkt.ip.IP_PROTO_OSPF} + + pkt_bld = CTRexPktBuilder() + if l2_encap == "ethernet_ii": + pkt_bld.add_pkt_layer("l2", dpkt.ethernet.Ethernet()) + # set Ethernet layer attributes + pkt_bld.set_eth_layer_addr("l2", "src", mac_src) + pkt_bld.set_eth_layer_addr("l2", "dst", mac_dst) + else: + raise NotImplementedError("l2_encap does not support the desired encapsulation '{0}'".format(l2_encap)) + # set l3 on l2 + if l3_protocol not in ALLOWED_L3_PROTOCOL: + raise ValueError("l3_protocol must be one of the following: {0}".format(ALLOWED_L3_PROTOCOL)) + pkt_bld.set_layer_attr("l2", "type", ALLOWED_L3_PROTOCOL[l3_protocol]) + + # set l3 attributes + if l3_protocol == "ipv4": + pkt_bld.add_pkt_layer("l3", dpkt.ip.IP()) + pkt_bld.set_ip_layer_addr("l3", "src", ip_src_addr) + pkt_bld.set_ip_layer_addr("l3", "dst", ip_dst_addr) + else: + raise NotImplementedError("l3_protocol '{0}' is not supported by TRex yet.".format(l3_protocol)) + + if __name__ == "__main__": pass -- cgit From 0c2b3c83f9cc0c25277c39660dce132aad55c3d7 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Wed, 28 Oct 2015 07:28:37 +0200 Subject: updated more HLTAPI functionality and fixed found bugs. Working: Start/stop traffic, traffic config (semi), connect, clean Missing: stats Next: boost console --- .../trex_control_plane/client/trex_hltapi.py | 158 ++++++++++++++++++--- .../client/trex_stateless_client.py | 36 +++-- .../client_utils/general_utils.py | 16 +++ .../trex_control_plane/common/trex_streams.py | 3 +- .../trex_control_plane/console/trex_console.py | 4 +- .../examples/stateless_example.py | 31 +++- 6 files changed, 213 insertions(+), 35 deletions(-) mode change 100644 => 100755 scripts/automation/trex_control_plane/examples/stateless_example.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_hltapi.py b/scripts/automation/trex_control_plane/client/trex_hltapi.py index cc7005e8..92768ca4 100755 --- a/scripts/automation/trex_control_plane/client/trex_hltapi.py +++ b/scripts/automation/trex_control_plane/client/trex_hltapi.py @@ -3,6 +3,8 @@ import trex_root_path from client_utils.packet_builder import CTRexPktBuilder from trex_stateless_client import CTRexStatelessClient +from common.trex_streams import * +from client_utils.general_utils import id_count_gen import dpkt @@ -11,8 +13,8 @@ class CTRexHltApi(object): def __init__(self): self.trex_client = None self.connected = False - - pass + # self._stream_db = CStreamList() + self._port_data = {} # ----- session functions ----- # @@ -50,11 +52,12 @@ class CTRexHltApi(object): port_handle = {device: {port: port # since only supporting single TRex at the moment, 1:1 map for port in port_list} } - ret_dict.update({"status": 1, "log": None, "port_handle": port_handle, "offline": 0}) # ports are online + self.connected = True + self._port_data_init(port_list) return ret_dict def cleanup_session(self, port_list, maintain_lock=False): @@ -75,6 +78,7 @@ class CTRexHltApi(object): "log": None}) self.trex_client.disconnect() self.trex_client = None + self.connected = False return ret_dict def interface_config(self, port_handle, mode="config"): @@ -86,37 +90,150 @@ class CTRexHltApi(object): # ----- traffic functions ----- # def traffic_config(self, mode, port_handle, - l2_encap, mac_src, mac_dst, - l3_protocol, ip_src_addr, ip_dst_addr, l3_length, - transmit_mode, rate_pps): + l2_encap="ethernet_ii", mac_src="00:00:01:00:00:01", mac_dst="00:00:00:00:00:00", + l3_protocol="ipv4", ip_src_addr="0.0.0.0", ip_dst_addr="192.0.0.1", l3_length=110, + transmit_mode="continuous", rate_pps=100, + **kwargs): ALLOWED_MODES = ["create", "modify", "remove", "enable", "disable", "reset"] if mode not in ALLOWED_MODES: raise ValueError("mode must be one of the following values: {modes}".format(modes=ALLOWED_MODES)) if mode == "create": - # create a new stream with desired attributes - stream = CTRexHltApi.generate_stream(l2_encap, mac_src, mac_dst, - l3_protocol, ip_src_addr, ip_dst_addr, l3_length, - transmit_mode, rate_pps) - - - - pass + # create a new stream with desired attributes, starting by creating packet + try: + packet = CTRexHltApi.generate_stream(l2_encap, mac_src, mac_dst, + l3_protocol, ip_src_addr, ip_dst_addr, l3_length) + # set transmission attributes + tx_mode = CTxMode(transmit_mode, rate_pps, **kwargs) + # set rx_stats + rx_stats = CRxStats() # defaults with disabled + # join the generated data into stream + stream_obj = CStream() + stream_obj_params = {"enabled": True, + "self_start": True, + "next_stream_id": -1, + "isg": 0.0, + "mode": tx_mode, + "rx_stats": rx_stats, + "packet": packet} # vm is excluded from this list since CTRexPktBuilder obj is passed + stream_obj.load_data(**stream_obj_params) + except Exception as e: + # some exception happened during the stream creation + return {"status": 0, "log": str(e)} + # try adding the stream, until free stream_id is found + port_data = self._port_data.get(port_handle) + id_candidate = None + # TODO: change this to better implementation + while True: + id_candidate = port_data["stream_id_gen"].next() + response = self.trex_client.add_stream(stream_id=id_candidate, + stream_obj=stream_obj, + port_id=port_handle) + res_ok, log = CTRexHltApi.process_response(port_handle, response) + if res_ok: + # found non-taken stream_id on server + # save it for modifying needs + port_data["streams"].update({id_candidate: stream_obj}) + break + else: + # proceed to another iteration to use another id + continue + return {"status": 1, + "stream_id": id_candidate, + "log": None} else: raise NotImplementedError("mode '{0}' is not supported yet on TRex".format(mode)) def traffic_control(self, action, port_handle): - pass + ALLOWED_ACTIONS = ["clear_stats", "run", "stop", "sync_run"] + if action not in ALLOWED_ACTIONS: + raise ValueError("action must be one of the following values: {actions}".format(actions=ALLOWED_ACTIONS)) + # ret_dict = {"status": 0, "stopped": 1} + port_list = self.parse_port_list(port_handle) + if action == "run": + response = self.trex_client.start_traffic(port_id=port_list) + res_ok, log = CTRexHltApi.process_response(port_list, response) + if res_ok: + return {"status": 1, + "stopped": 0, + "log": None} + elif action == "stop": + response = self.trex_client.stop_traffic(port_id=port_list) + res_ok, log = CTRexHltApi.process_response(port_list, response) + if res_ok: + return {"status": 1, + "stopped": 1, + "log": None} + else: + raise NotImplementedError("action '{0}' is not supported yet on TRex".format(action)) + + # if we arrived here, this means that operation FAILED! + return {"status": 0, + "log": log} + def traffic_stats(self, port_handle, mode): - pass + ALLOWED_MODES = ["aggregate", "streams", "all"] + if mode not in ALLOWED_MODES: + raise ValueError("mode must be one of the following values: {modes}".format(modes=ALLOWED_MODES)) + # pass this function for now... + if mode == "aggregate": + # create a new stream with desired attributes, starting by creating packet + try: + packet = CTRexHltApi.generate_stream(l2_encap, mac_src, mac_dst, + l3_protocol, ip_src_addr, ip_dst_addr, l3_length) + # set transmission attributes + tx_mode = CTxMode(transmit_mode, rate_pps, **kwargs) + # set rx_stats + rx_stats = CRxStats() # defaults with disabled + # join the generated data into stream + stream_obj = CStream() + stream_obj_params = {"enabled": True, + "self_start": True, + "next_stream_id": -1, + "isg": 0.0, + "mode": tx_mode, + "rx_stats": rx_stats, + "packet": packet} # vm is excluded from this list since CTRexPktBuilder obj is passed + stream_obj.load_data(**stream_obj_params) + except Exception as e: + # some exception happened during the stream creation + return {"status": 0, "log": str(e)} + # try adding the stream, until free stream_id is found + port_data = self._port_data.get(port_handle) + id_candidate = None + # TODO: change this to better implementation + while True: + id_candidate = port_data["stream_id_gen"].next() + response = self.trex_client.add_stream(stream_id=id_candidate, + stream_obj=stream_obj, + port_id=port_handle) + res_ok, log = CTRexHltApi.process_response(port_handle, response) + if res_ok: + # found non-taken stream_id on server + # save it for modifying needs + port_data["streams"].update({id_candidate: stream_obj}) + break + else: + # proceed to another iteration to use another id + continue + return {"status": 1, + "stream_id": id_candidate, + "log": None} + else: + raise NotImplementedError("mode '{0}' is not supported yet on TRex".format(mode)) def get_aggregate_port_stats(self, port_handle): return self.traffic_stats(port_handle, mode="aggregate") def get_stream_stats(self, port_handle): - return self.traffic_stats(port_handle, mode="stream") + return self.traffic_stats(port_handle, mode="streams") # ----- internal functions ----- # + def _port_data_init(self, port_list): + for port in port_list: + self._port_data[port] = {"stream_id_gen": id_count_gen(), + "streams": {}} + @staticmethod def process_response(port_list, response): if isinstance(port_list, list): @@ -145,8 +262,7 @@ class CTRexHltApi(object): @staticmethod def generate_stream(l2_encap, mac_src, mac_dst, - l3_protocol, ip_src_addr, ip_dst_addr, l3_length, - transmit_mode, rate_pps): + l3_protocol, ip_src_addr, ip_dst_addr, l3_length): ALLOWED_L3_PROTOCOL = {"ipv4": dpkt.ethernet.ETH_TYPE_IP, "ipv6": dpkt.ethernet.ETH_TYPE_IP6, "arp": dpkt.ethernet.ETH_TYPE_ARP} @@ -177,9 +293,13 @@ class CTRexHltApi(object): pkt_bld.add_pkt_layer("l3", dpkt.ip.IP()) pkt_bld.set_ip_layer_addr("l3", "src", ip_src_addr) pkt_bld.set_ip_layer_addr("l3", "dst", ip_dst_addr) + pkt_bld.set_layer_attr("l3", "len", l3_length) else: raise NotImplementedError("l3_protocol '{0}' is not supported by TRex yet.".format(l3_protocol)) + pkt_bld.dump_pkt_to_pcap("stream_test.pcap") + return pkt_bld + if __name__ == "__main__": diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 3079ee5e..bbdcddbe 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -62,7 +62,7 @@ class CTRexStatelessClient(object): continue if bad_ids: # Some port IDs are not according to desires status - raise RuntimeError("The requested method ('{0}') cannot be invoked since port IDs {1} are not" + raise RuntimeError("The requested method ('{0}') cannot be invoked since port IDs {1} are not " "at allowed stated".format(func.__name__, list(bad_ids))) else: return func(self, *args, **kwargs) @@ -229,13 +229,15 @@ class CTRexStatelessClient(object): for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) if rc: - self._process_batch_result(commands, resp_list, self._handle_start_traffic_response) + return self._process_batch_result(commands, resp_list, self._handle_start_traffic_response, + success_test=self.ack_success_test) else: params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} command = RpcCmdData("start_traffic", params) - self._handle_start_traffic_response(command, self.transmit(command.method, command.params)) - return + return self._handle_start_traffic_response(command, + self.transmit(command.method, command.params), + self.ack_success_test) @force_status(owned=False, active_and_owned=True) def stop_traffic(self, port_id=None): @@ -248,13 +250,15 @@ class CTRexStatelessClient(object): for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) if rc: - self._process_batch_result(commands, resp_list, self._handle_stop_traffic_response) + return self._process_batch_result(commands, resp_list, self._handle_stop_traffic_response, + success_test=self.ack_success_test) else: params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} command = RpcCmdData("stop_traffic", params) - self._handle_start_traffic_response(command, self.transmit(command.method, command.params)) - return + return self._handle_start_traffic_response(command, + self.transmit(command.method, command.params), + self.ack_success_test) def get_global_stats(self): command = RpcCmdData("get_global_stats", {}) @@ -375,12 +379,20 @@ class CTRexStatelessClient(object): return RpcResponseStatus(False, port_id, response.data) def _handle_start_traffic_response(self, request, response, success_test): - if response.success: - self._active_ports.add(request.get("port_id")) + port_id = request.params.get("port_id") + if success_test(response): + self._active_ports.add(port_id) + return RpcResponseStatus(True, port_id, "Traffic started") + else: + return RpcResponseStatus(False, port_id, response.data) - def _handle_stop_traffic_response(self, request, response): - if response.success: - self._active_ports.remove(request.get("port_id")) + def _handle_stop_traffic_response(self, request, response, success_test): + port_id = request.params.get("port_id") + if success_test(response): + self._active_ports.remove(port_id) + return RpcResponseStatus(True, port_id, "Traffic stopped") + else: + return RpcResponseStatus(False, port_id, response.data) def _handle_get_global_stats_response(self, request, response, success_test): if response.success: diff --git a/scripts/automation/trex_control_plane/client_utils/general_utils.py b/scripts/automation/trex_control_plane/client_utils/general_utils.py index 5488b9dd..3c025608 100755 --- a/scripts/automation/trex_control_plane/client_utils/general_utils.py +++ b/scripts/automation/trex_control_plane/client_utils/general_utils.py @@ -75,6 +75,22 @@ def random_id_gen(length=8): return_id += random.choice(id_chars) yield return_id +def id_count_gen(): + """ + A generator for creating an increasing id for objects, starting from 0 + + :parameters: + None + + :return: + an id (unsigned int) with each next() request. + """ + return_id = 0 + while True: + yield return_id + return_id += 1 + + if __name__ == "__main__": pass diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py index 750da198..bb4c72ca 100755 --- a/scripts/automation/trex_control_plane/common/trex_streams.py +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -23,7 +23,7 @@ class CStreamList(object): if name in self.streams_list: raise NameError("A stream with this name already exists on this list.") self.streams_list[name]=stream_obj - return + return name def remove_stream(self, name): popped = self.streams_list.pop(name) @@ -184,6 +184,7 @@ class CStream(object): if isinstance(kwargs[k], CTRexPktBuilder): if "vm" not in kwargs: self.load_packet_obj(kwargs[k]) + break # vm field check is skipped else: raise ValueError("When providing packet object with a CTRexPktBuilder, vm parameter " "should not be supplied") diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 4f9743f4..2ea29473 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -449,10 +449,10 @@ class TRexConsole(cmd.Cmd): try: compiled_streams = stream_list.compile_streams() self.user_streams[name] = LoadedStreamList(loaded_obj, - [StreamPack(v.stream_id, v.stream.dump_compiled()) + [StreamPack(v.stream_id, v.stream.dump()) for k, v in compiled_streams.items()]) - print "Stream '{0}' loaded successfully".format(name) + print "Stream list '{0}' loaded successfully".format(name) except Exception as e: raise return diff --git a/scripts/automation/trex_control_plane/examples/stateless_example.py b/scripts/automation/trex_control_plane/examples/stateless_example.py old mode 100644 new mode 100755 index f97bce4c..bb0fe983 --- a/scripts/automation/trex_control_plane/examples/stateless_example.py +++ b/scripts/automation/trex_control_plane/examples/stateless_example.py @@ -1 +1,30 @@ -__author__ = 'danklei' +#!/router/bin/python + +import trex_root_path +from client.trex_hltapi import CTRexHltApi + +if __name__ == "__main__": + port_list = [1,2] + try: + hlt_client = CTRexHltApi() + con = hlt_client.connect("localhost", port_list, "danklei", break_locks=True, reset=True)#, port=6666) + print con + + res = hlt_client.traffic_config("create", 1)#, ip_src_addr="2000.2.2") + print res + res = hlt_client.traffic_config("create", 2)#, ip_src_addr="2000.2.2") + print res + + res = hlt_client.traffic_control("run", [1, 2])#, ip_src_addr="2000.2.2") + print res + + res = hlt_client.traffic_control("stop", [1, 2])#, ip_src_addr="2000.2.2") + print res + + + + except Exception as e: + raise + finally: + res = hlt_client.cleanup_session(port_list) + print res \ No newline at end of file -- cgit From f963facdc949c087c863b8ad81aae537bcf3767b Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Wed, 28 Oct 2015 23:59:11 +0200 Subject: console progress, still SHAKY! --- .../client/trex_stateless_client.py | 21 +- .../trex_control_plane/console/trex_console.py | 238 ++++++++++++--------- 2 files changed, 151 insertions(+), 108 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index bbdcddbe..1df07e2a 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -89,6 +89,9 @@ class CTRexStatelessClient(object): self.__err_log = ver_info return self._server_version if self._server_version else "Unknown" + def is_connected(self): + return self.comm_link.is_connected + # ----- user-access methods ----- # def connect(self): rc, err = self.comm_link.connect() @@ -114,6 +117,13 @@ class CTRexStatelessClient(object): def get_port_count(self): return self.system_info.get("port_count") + def get_port_ids(self, as_str=False): + port_ids = range(self.get_port_count()) + if as_str: + return " ".join(str(p) for p in port_ids) + else: + return port_ids + def get_acquired_ports(self): return self._conn_handler.keys() @@ -417,7 +427,7 @@ class CTRexStatelessClient(object): # check each item of the sequence return all([self._is_ports_valid(port) for port in port_id]) - elif (isinstance(port_id, int)) and (port_id > 0) and (port_id <= self.get_port_count()): + elif (isinstance(port_id, int)) and (port_id >= 0) and (port_id <= self.get_port_count()): return True else: return False @@ -450,13 +460,20 @@ class CTRexStatelessClient(object): self.port = port self.rpc_link = JsonRpcClient(self.server, self.port) + @property + def is_connected(self): + if not self.virtual: + return self.rpc_link.connected + else: + return True + def connect(self): if not self.virtual: return self.rpc_link.connect() def disconnect(self): if not self.virtual: - self.rpc_link.disconnect() + return self.rpc_link.disconnect() def transmit(self, method_name, params={}): if self.virtual: diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 2ea29473..241d46b9 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -29,6 +29,7 @@ import sys import tty, termios import trex_root_path from common.trex_streams import * +from client.trex_stateless_client import CTRexStatelessClient from client_utils.jsonrpc_client import TrexStatelessClient @@ -75,6 +76,25 @@ class YesNoMenu(object): else: return False +class CStreamsDB(object): + + def __init__(self): + self.loaded_streams = {} + + def load_streams(self, name, LoadedStreamList_obj): + if name in self.loaded_streams: + return False + else: + self.loaded_streams[name] = LoadedStreamList_obj + return True + + def remove_streams(self, name): + return self.loaded_streams.pop(name) + + def get_loaded_streams(self): + return self.loaded_streams.keys() + + # multi level cmd menu class CmdMenu(object): def __init__ (self): @@ -125,10 +145,10 @@ class AddStreamMenu(CmdMenu): class TRexConsole(cmd.Cmd): """Trex Console""" - def __init__(self, rpc_client): + def __init__(self, stateless_client, verbose): cmd.Cmd.__init__(self) - self.rpc_client = rpc_client + self.stateless_client = stateless_client self.do_connect("") @@ -140,6 +160,7 @@ class TRexConsole(cmd.Cmd): self.postcmd(False, "") self.user_streams = {} + self.streams_db = CStreamsDB() # a cool hack - i stole this function and added space @@ -155,12 +176,12 @@ class TRexConsole(cmd.Cmd): elif line == "on": self.verbose = True - self.rpc_client.set_verbose(True) + self.stateless_client.set_verbose(True) print "\nverbose set to on\n" elif line == "off": self.verbose = False - self.rpc_client.set_verbose(False) + self.stateless_client.set_verbose(False) print "\nverbose set to off\n" else: @@ -170,7 +191,7 @@ class TRexConsole(cmd.Cmd): def do_query_server(self, line): '''query the RPC server for supported remote commands\n''' - rc, msg = self.rpc_client.query_rpc_server() + rc, msg = self.stateless_client.query_rpc_server() if not rc: print "\n*** " + msg + "\n" return @@ -186,8 +207,8 @@ class TRexConsole(cmd.Cmd): print "\n-> Pinging RPC server" - rc, msg = self.rpc_client.ping_rpc_server() - if rc: + res_ok, msg = self.stateless_client.ping() + if res_ok: print "[SUCCESS]\n" else: print "\n*** " + msg + "\n" @@ -198,13 +219,15 @@ class TRexConsole(cmd.Cmd): self.do_acquire(line, True) + def extract_port_ids_from_line(self, line): + return {int(x) for x in line.split()} + def parse_ports_from_line (self, line): port_list = set() - if line: for port_id in line.split(' '): - if (not port_id.isdigit()) or (int(port_id) < 0) or (int(port_id) >= self.rpc_client.get_port_count()): - print "Please provide a list of ports seperated by spaces between 0 and {0}".format(self.rpc_client.get_port_count() - 1) + if (not port_id.isdigit()) or (int(port_id) < 0) or (int(port_id) >= self.stateless_client.get_port_count()): + print "Please provide a list of ports separated by spaces between 0 and {0}".format(self.stateless_client.get_port_count() - 1) return None port_list.add(int(port_id)) @@ -212,106 +235,86 @@ class TRexConsole(cmd.Cmd): port_list = list(port_list) else: - port_list = [i for i in xrange(0, self.rpc_client.get_port_count())] + port_list = [i for i in xrange(0, self.stateless_client.get_port_count())] return port_list - def do_acquire (self, line, force = False): + def do_acquire (self, line, force=False): '''Acquire ports\n''' # make sure that the user wants to acquire all - if line == "": - ask = YesNoMenu('Do you want to acquire all ports ? ') + args = line.split() + if len(args) < 1: + print "Please provide a list of ports separated by spaces, or specify 'all' to acquire all available ports" + if args[0] == "all": + ask = YesNoMenu('Are you sure you want to acquire all ports ? ') rc = ask.show() if rc == False: return - - port_list = self.parse_ports_from_line(line) - if not port_list: - return + else: + port_list = self.stateless_client.get_port_ids() + else: + port_list = self.extract_port_ids_from_line(line) print "\nTrying to acquire ports: " + (" ".join(str(x) for x in port_list)) + "\n" - rc, resp_list = self.rpc_client.take_ownership(port_list, force) - - if not rc: - print "\n*** " + resp_list + "\n" + # rc, resp_list = self.stateless_client.take_ownership(port_list, force) + res_ok, log = self.stateless_client.acquire(port_list, force) + self.prompt_response(log) + if not res_ok: + print "[FAILED]\n" return - - for i, rc in enumerate(resp_list): - if rc[0]: - print "Port {0} - Acquired".format(port_list[i]) - else: - print "Port {0} - ".format(port_list[i]) + rc[1] - - print "\n" + print "[SUCCESS]\n" + return def do_release (self, line): '''Release ports\n''' - if line: - port_list = self.parse_ports_from_line(line) - else: - port_list = self.rpc_client.get_owned_ports() - - if not port_list: - return - - rc, resp_list = self.rpc_client.release_ports(port_list) - - - print "\n" - - for i, rc in enumerate(resp_list): - if rc[0]: - print "Port {0} - Released".format(port_list[i]) + # if line: + # port_list = self.parse_ports_from_line(line) + # else: + # port_list = self.stateless_client.get_owned_ports() + args = line.split() + if len(args) < 1: + print "Please provide a list of ports separated by spaces, or specify 'all' to acquire all available ports" + if args[0] == "all": + ask = YesNoMenu('Are you sure you want to release all acquired ports ? ') + rc = ask.show() + if rc == False: + return else: - print "Port {0} - Failed to release port, probably not owned by you or port is under traffic" - - print "\n" - - def do_get_port_stats (self, line): - '''Get ports stats\n''' - - port_list = self.parse_ports_from_line(line) - if not port_list: - return - - rc, resp_list = self.rpc_client.get_port_stats(port_list) + port_list = self.stateless_client.get_acquired_ports() + else: + port_list = self.extract_port_ids_from_line(line) - if not rc: - print "\n*** " + resp_list + "\n" + res_ok, log = self.stateless_client.release(port_list) + self.prompt_response(log) + if not res_ok: + print "[FAILED]\n" return - - for i, rc in enumerate(resp_list): - if rc[0]: - print "\nPort {0} stats:\n{1}\n".format(port_list[i], self.rpc_client.pretty_json(json.dumps(rc[1]))) - else: - print "\nPort {0} - ".format(i) + rc[1] + "\n" - - print "\n" - + print "[SUCCESS]\n" + return def do_connect (self, line): '''Connects to the server\n''' if line == "": - rc, msg = self.rpc_client.connect() + res_ok, msg = self.stateless_client.connect() else: sp = line.split() if (len(sp) != 2): print "\n[usage] connect [server] [port] or without parameters\n" return - rc, msg = self.rpc_client.connect(sp[0], sp[1]) + res_ok, msg = self.stateless_client.connect(sp[0], sp[1]) - if rc: + if res_ok: print "[SUCCESS]\n" else: print "\n*** " + msg + "\n" return - self.supported_rpc = self.rpc_client.get_supported_cmds() + self.supported_rpc = self.stateless_client.get_supported_cmds().data def do_rpc (self, line): '''Launches a RPC on the server\n''' @@ -344,9 +347,9 @@ class TRexConsole(cmd.Cmd): print "Example: rpc test_add {'x': 12, 'y': 17}\n" return - rc, msg = self.rpc_client.invoke_rpc_method(method, params) - if rc: - print "\nServer Response:\n\n" + self.rpc_client.pretty_json(json.dumps(msg)) + "\n" + res_ok, msg = self.stateless_client.invoke_rpc_method(method, params) + if res_ok: + print "\nServer Response:\n\n" + self.stateless_client.pretty_json(json.dumps(msg)) + "\n" else: print "\n*** " + msg + "\n" #print "Please try 'reconnect' to reconnect to server" @@ -359,7 +362,7 @@ class TRexConsole(cmd.Cmd): '''Shows a graphical console\n''' self.do_verbose('off') - trex_status.show_trex_status(self.rpc_client) + trex_status.show_trex_status(self.stateless_client) def do_quit(self, line): '''Exit the client\n''' @@ -367,22 +370,22 @@ class TRexConsole(cmd.Cmd): def do_disconnect (self, line): '''Disconnect from the server\n''' - if not self.rpc_client.is_connected(): + if not self.stateless_client.is_connected(): print "Not connected to server\n" return - rc, msg = self.rpc_client.disconnect() - if rc: + res_ok, msg = self.stateless_client.disconnect() + if res_ok: print "[SUCCESS]\n" else: print msg + "\n" def do_whoami (self, line): '''Prints console user name\n''' - print "\n" + self.rpc_client.whoami() + "\n" + print "\n" + self.stateless_client.user + "\n" def postcmd(self, stop, line): - if self.rpc_client.is_connected(): + if self.stateless_client.is_connected(): self.prompt = "TRex > " else: self.supported_rpc = None @@ -430,7 +433,7 @@ class TRexConsole(cmd.Cmd): print "{:<30} {:<30}".format(cmd + " - ", help) - def do_load_stream_list(self, line): + def do_stream_db_add(self, line): '''Loads a YAML stream list serialization into user console \n''' args = line.split() if args >= 2: @@ -442,19 +445,31 @@ class TRexConsole(cmd.Cmd): multiplier = 1 stream_list = CStreamList() loaded_obj = stream_list.load_yaml(yaml_path, multiplier) - # print self.rpc_client.pretty_json(json.dumps(loaded_obj)) - if name in self.user_streams: - print "Picked name already exist. Please pick another name." - else: - try: - compiled_streams = stream_list.compile_streams() - self.user_streams[name] = LoadedStreamList(loaded_obj, - [StreamPack(v.stream_id, v.stream.dump()) - for k, v in compiled_streams.items()]) - + # print self.stateless_client.pretty_json(json.dumps(loaded_obj)) + try: + compiled_streams = stream_list.compile_streams() + res_ok = self.streams_db.load_streams(name, LoadedStreamList(loaded_obj, + [StreamPack(v.stream_id, v.stream.dump()) + for k, v in compiled_streams.items()])) + if res_ok: print "Stream list '{0}' loaded successfully".format(name) - except Exception as e: - raise + else: + print "Picked name already exist. Please pick another name." + except Exception as e: + print "adding new stream failed due to the following error:\n", str(e) + + # if name in self.user_streams: + # print "Picked name already exist. Please pick another name." + # else: + # try: + # compiled_streams = stream_list.compile_streams() + # self.user_streams[name] = LoadedStreamList(loaded_obj, + # [StreamPack(v.stream_id, v.stream.dump()) + # for k, v in compiled_streams.items()]) + # + # print "Stream list '{0}' loaded successfully".format(name) + # except Exception as e: + # raise return else: print "please provide load name and YAML path, separated by space.\n" \ @@ -473,7 +488,7 @@ class TRexConsole(cmd.Cmd): if x.startswith(start_string)] - def complete_load_stream_list(self, text, line, begidx, endidx): + def complete_stream_db_add(self, text, line, begidx, endidx): arg_num = len(line.split()) - 1 if arg_num == 2: return TRexConsole.tree_autocomplete(line.split()[-1]) @@ -488,9 +503,9 @@ class TRexConsole(cmd.Cmd): try: stream = self.user_streams[list_name] if len(args) >= 2 and args[1] == "full": - print self.rpc_client.pretty_json(json.dumps(stream.compiled)) + print self.stateless_client.pretty_json(json.dumps(stream.compiled)) else: - print self.rpc_client.pretty_json(json.dumps(stream.loaded)) + print self.stateless_client.pretty_json(json.dumps(stream.loaded)) except KeyError as e: print "Unknown stream list name provided" else: @@ -508,9 +523,9 @@ class TRexConsole(cmd.Cmd): try: stream_list = self.user_streams[args[0]] port_list = self.parse_ports_from_line(' '.join(args[1:])) - owned = set(self.rpc_client.get_owned_ports()) + owned = set(self.stateless_client.get_owned_ports()) if set(port_list).issubset(owned): - rc, resp_list = self.rpc_client.add_stream(port_list, stream_list.compiled) + rc, resp_list = self.stateless_client.add_stream(port_list, stream_list.compiled) if not rc: print "\n*** " + resp_list + "\n" return @@ -528,6 +543,12 @@ class TRexConsole(cmd.Cmd): print "Please provide list name and ports to attach to, or leave empty to attach to all ports." + def prompt_response(self, response_obj): + resp_list = response_obj if isinstance(response_obj, list) else [response_obj] + for response in resp_list: + print response + + @@ -549,9 +570,9 @@ class TRexConsole(cmd.Cmd): stream_id = int(params[1]) packet = [0xFF,0xFF,0xFF] - rc, msg = self.rpc_client.add_stream(port_id = port_id, stream_id = stream_id, isg = 1.1, next_stream_id = -1, packet = packet) + rc, msg = self.stateless_client.add_stream(port_id = port_id, stream_id = stream_id, isg = 1.1, next_stream_id = -1, packet = packet) if rc: - print "\nServer Response:\n\n" + self.rpc_client.pretty_json(json.dumps(msg)) + "\n" + print "\nServer Response:\n\n" + self.stateless_client.pretty_json(json.dumps(msg)) + "\n" else: print "\n*** " + msg + "\n" @@ -573,18 +594,23 @@ def setParserOptions (): default = 'user_' + ''.join(random.choice(string.digits) for _ in range(5)), type = str) + parser.add_argument("--verbose", dest="verbose", + action="store_true", help="Switch ON verbose option. Default is: OFF.", + default = False) + return parser def main (): parser = setParserOptions() - options = parser.parse_args(sys.argv[1:]) + options = parser.parse_args()#sys.argv[1:]) - # RPC client - rpc_client = TrexStatelessClient(options.server, options.port, options.user) + # Stateless client connection + # stateless_client = TrexStatelessClient(options.server, options.port, options.user) + stateless_client = CTRexStatelessClient(options.user, options.server, options.port) # console try: - console = TRexConsole(rpc_client) + console = TRexConsole(stateless_client, options.verbose) console.cmdloop() except KeyboardInterrupt as e: print "\n\n*** Caught Ctrl + C... Exiting...\n\n" -- cgit From 5377774afbf00b0da0ad0b74d3be207f6eb9124e Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 29 Oct 2015 07:54:21 +0200 Subject: Added module for text formatting, such that coloring, bold, underline. --- .../trex_control_plane/common/text_opts.py | 72 ++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 scripts/automation/trex_control_plane/common/text_opts.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/common/text_opts.py b/scripts/automation/trex_control_plane/common/text_opts.py new file mode 100644 index 00000000..8ab03eeb --- /dev/null +++ b/scripts/automation/trex_control_plane/common/text_opts.py @@ -0,0 +1,72 @@ + + +TEXT_CODES = {'bold': {'start': '\x1b[1m', + 'end': '\x1b[22m'}, + 'cyan': {'start': '\x1b[36m', + 'end': '\x1b[39m'}, + 'blue': {'start': '\x1b[34m', + 'end': '\x1b[39m'}, + 'red': {'start': '\x1b[31m', + 'end': '\x1b[39m'}, + 'magenta': {'start': '\x1b[35m', + 'end': '\x1b[39m'}, + 'green': {'start': '\x1b[32m', + 'end': '\x1b[39m'}, + 'underline': {'start': '\x1b[4m', + 'end': '\x1b[24m'}} + + +def bold(text): + return text_attribute(text, 'bold') + + +def cyan(text): + return text_attribute(text, 'cyan') + + +def blue(text): + return text_attribute(text, 'blue') + + +def red(text): + return text_attribute(text, 'red') + + +def magenta(text): + return text_attribute(text, 'magenta') + + +def green(text): + return text_attribute(text, 'green') + + +def underline(text): + return text_attribute(text, 'underline') + + +def text_attribute(text, attribute): + return "{start}{txt}{stop}".format(start=TEXT_CODES[attribute]['start'], + txt=text, + stop=TEXT_CODES[attribute]['end']) + + +FUNC_DICT = {'blue': blue, + 'bold': bold, + 'green': green, + 'cyan': cyan, + 'magenta': magenta, + 'underline': underline, + 'red': red} + + +def format_text(text, *args): + return_string = text + for i in args: + func = FUNC_DICT.get(i) + if func: + return_string = func(return_string) + return return_string + + +if __name__ == "__main__": + pass -- cgit From aa37a0abb00cbf4cb1611f9c0eefcb1ab850bc45 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 29 Oct 2015 11:34:03 +0200 Subject: Console redesign using trex_stateless_client module --- .../client/trex_stateless_client.py | 29 +++ .../trex_control_plane/common/text_opts.py | 27 ++- .../trex_control_plane/console/trex_console.py | 234 +++++++++++++++------ 3 files changed, 227 insertions(+), 63 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 1df07e2a..0fff9b36 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -180,6 +180,27 @@ class CTRexStatelessClient(object): "stream": stream_obj.dump()} return self.transmit("add_stream", params) + @force_status(owned=True) + def add_stream_pack(self, port_id=None, *stream_packs): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + + # since almost every run contains more than one transaction with server, handle all as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [] + for stream_pack in stream_packs: + commands.extend([RpcCmdData("add_stream", {"port_id": p_id, + "handler": self._conn_handler.get(p_id), + "stream_id": stream_pack.stream_id, + "stream": stream_pack.stream} + ) + for p_id in port_ids] + ) + res_ok, resp_list = self.transmit_batch(commands) + if res_ok: + return self._process_batch_result(commands, resp_list, self._handle_add_stream_response, + success_test=self.ack_success_test) + @force_status(owned=True) def remove_stream(self, stream_id, port_id=None): if not self._is_ports_valid(port_id): @@ -373,6 +394,14 @@ class CTRexStatelessClient(object): else: return RpcResponseStatus(False, port_id, response.data) + def _handle_add_stream_response(self, request, response, success_test): + port_id = request.params.get("port_id") + stream_id = request.params.get("stream_id") + if success_test(response): + return RpcResponseStatus(True, port_id, "Stream {0} added".format(stream_id)) + else: + return RpcResponseStatus(False, port_id, response.data) + def _handle_remove_streams_response(self, request, response, success_test): port_id = request.params.get("port_id") if success_test(response): diff --git a/scripts/automation/trex_control_plane/common/text_opts.py b/scripts/automation/trex_control_plane/common/text_opts.py index 8ab03eeb..0d8323db 100644 --- a/scripts/automation/trex_control_plane/common/text_opts.py +++ b/scripts/automation/trex_control_plane/common/text_opts.py @@ -1,4 +1,5 @@ - +import json +import re TEXT_CODES = {'bold': {'start': '\x1b[1m', 'end': '\x1b[22m'}, @@ -65,7 +66,29 @@ def format_text(text, *args): func = FUNC_DICT.get(i) if func: return_string = func(return_string) - return return_string + return + +# pretty print for JSON +def pretty_json (json_str, use_colors = True): + pretty_str = json.dumps(json.loads(json_str), indent = 4, separators=(',', ': '), sort_keys = True) + + if not use_colors: + return pretty_str + + try: + # int numbers + pretty_str = re.sub(r'([ ]*:[ ]+)(\-?[1-9][0-9]*[^.])',r'\1{0}'.format(blue(r'\2')), pretty_str) + # float + pretty_str = re.sub(r'([ ]*:[ ]+)(\-?[1-9][0-9]*\.[0-9]+)',r'\1{0}'.format(magenta(r'\2')), pretty_str) + # # strings + # + pretty_str = re.sub(r'([ ]*:[ ]+)("[^"]*")',r'\1{0}'.format(red(r'\2')), pretty_str) + pretty_str = re.sub(r"('[^']*')", r'{0}\1{1}'.format(TEXT_CODES['magenta']['start'], + TEXT_CODES['red']['start']), pretty_str) + except : + pass + + return pretty_str if __name__ == "__main__": diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 241d46b9..d2050f09 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -30,6 +30,8 @@ import tty, termios import trex_root_path from common.trex_streams import * from client.trex_stateless_client import CTRexStatelessClient +from common.text_opts import * +from client_utils.general_utils import user_input from client_utils.jsonrpc_client import TrexStatelessClient @@ -43,7 +45,7 @@ LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) # def readch(choices=[]): - + fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: @@ -69,30 +71,51 @@ class YesNoMenu(object): ch = readch(choices = ['y', 'Y', 'n', 'N']) if ch == None: return None - - print "\n" + + print "\n" if ch == 'y' or ch == 'Y': return True else: return False +class ConfirmMenu(object): + def __init__ (self, caption): + self.caption = "{cap} [confirm] : ".format(cap=caption) + + def show(self): + sys.stdout.write(self.caption) + + + + class CStreamsDB(object): def __init__(self): - self.loaded_streams = {} + self.stream_packs = {} def load_streams(self, name, LoadedStreamList_obj): - if name in self.loaded_streams: + if name in self.stream_packs: return False else: - self.loaded_streams[name] = LoadedStreamList_obj + self.stream_packs[name] = LoadedStreamList_obj return True - def remove_streams(self, name): - return self.loaded_streams.pop(name) + def remove_stream_packs(self, *names): + removed_streams = [] + for name in names: + removed = self.stream_packs.pop(name) + if removed: + removed_streams.append(name) + return removed_streams + + def clear(self): + self.stream_packs.clear() - def get_loaded_streams(self): - return self.loaded_streams.keys() + def get_loaded_streams_names(self): + return self.stream_packs.keys() + + def get_stream_pack(self, name): + return self.stream_packs.get(name) # multi level cmd menu @@ -144,7 +167,7 @@ class AddStreamMenu(CmdMenu): # main console object class TRexConsole(cmd.Cmd): """Trex Console""" - + def __init__(self, stateless_client, verbose): cmd.Cmd.__init__(self) @@ -153,7 +176,7 @@ class TRexConsole(cmd.Cmd): self.do_connect("") self.intro = "\n-=TRex Console v{ver}=-\n".format(ver=__version__) - self.intro += "\nType 'help' or '?' for supported actions\n" + self.intro += "\nType 'help' or '?' for supported actions\n" self.verbose = False @@ -161,7 +184,7 @@ class TRexConsole(cmd.Cmd): self.user_streams = {} self.streams_db = CStreamsDB() - + # a cool hack - i stole this function and added space def completenames(self, text, *ignored): @@ -209,7 +232,7 @@ class TRexConsole(cmd.Cmd): res_ok, msg = self.stateless_client.ping() if res_ok: - print "[SUCCESS]\n" + print format_text("[SUCCESS]\n", 'green', 'bold') else: print "\n*** " + msg + "\n" return @@ -262,9 +285,9 @@ class TRexConsole(cmd.Cmd): res_ok, log = self.stateless_client.acquire(port_list, force) self.prompt_response(log) if not res_ok: - print "[FAILED]\n" + print format_text("[FAILED]\n", 'red', 'bold') return - print "[SUCCESS]\n" + print format_text("[SUCCESS]\n", 'green', 'bold') return def do_release (self, line): @@ -290,9 +313,9 @@ class TRexConsole(cmd.Cmd): res_ok, log = self.stateless_client.release(port_list) self.prompt_response(log) if not res_ok: - print "[FAILED]\n" + print format_text("[FAILED]\n", 'red', 'bold') return - print "[SUCCESS]\n" + print format_text("[SUCCESS]\n", 'green', 'bold') return def do_connect (self, line): @@ -356,7 +379,9 @@ class TRexConsole(cmd.Cmd): def complete_rpc (self, text, line, begidx, endidx): - return [x for x in self.supported_rpc if x.startswith(text)] + return [x + for x in self.supported_rpc + if x.startswith(text)] def do_status (self, line): '''Shows a graphical console\n''' @@ -376,14 +401,14 @@ class TRexConsole(cmd.Cmd): res_ok, msg = self.stateless_client.disconnect() if res_ok: - print "[SUCCESS]\n" + print format_text("[SUCCESS]\n", 'green', 'bold') else: print msg + "\n" def do_whoami (self, line): '''Prints console user name\n''' print "\n" + self.stateless_client.user + "\n" - + def postcmd(self, stop, line): if self.stateless_client.is_connected(): self.prompt = "TRex > " @@ -452,11 +477,12 @@ class TRexConsole(cmd.Cmd): [StreamPack(v.stream_id, v.stream.dump()) for k, v in compiled_streams.items()])) if res_ok: - print "Stream list '{0}' loaded successfully".format(name) + print green("Stream list '{0}' loaded and added successfully".format(name)) else: - print "Picked name already exist. Please pick another name." + print magenta("Picked name already exist. Please pick another name.") except Exception as e: print "adding new stream failed due to the following error:\n", str(e) + print format_text("[FAILED]\n", 'red', 'bold') # if name in self.user_streams: # print "Picked name already exist. Please pick another name." @@ -495,71 +521,157 @@ class TRexConsole(cmd.Cmd): else: return [text] - def do_show_stream_list(self, line): + def do_stream_db_show(self, line): '''Shows the loaded stream list named [name] \n''' args = line.split() if args: list_name = args[0] try: - stream = self.user_streams[list_name] + stream = self.streams_db.get_stream_pack(list_name)#user_streams[list_name] if len(args) >= 2 and args[1] == "full": - print self.stateless_client.pretty_json(json.dumps(stream.compiled)) + print pretty_json(json.dumps(stream.compiled)) else: - print self.stateless_client.pretty_json(json.dumps(stream.loaded)) + print pretty_json(json.dumps(stream.loaded)) except KeyError as e: print "Unknown stream list name provided" else: - print "\nAvailable stream lists:\n{0}".format(', '.join([x - for x in self.user_streams.keys()])) + print "\nAvailable stream lists:\n{0}".format(', '.join(sorted(self.streams_db.get_loaded_streams_names()))) - def complete_show_stream_list(self, text, line, begidx, endidx): + def complete_stream_db_show(self, text, line, begidx, endidx): return [x - for x in self.user_streams.keys() + for x in self.streams_db.get_loaded_streams_names() if x.startswith(text)] - def do_attach(self, line): + def do_stream_db_remove(self, line): args = line.split() - if len(args) >= 1: - try: - stream_list = self.user_streams[args[0]] - port_list = self.parse_ports_from_line(' '.join(args[1:])) - owned = set(self.stateless_client.get_owned_ports()) - if set(port_list).issubset(owned): - rc, resp_list = self.stateless_client.add_stream(port_list, stream_list.compiled) - if not rc: - print "\n*** " + resp_list + "\n" - return - else: - print "Not all desired ports are aquired.\n" \ - "Acquired ports are: {acq}\n" \ - "Requested ports: {req}\n" \ - "Missing ports: {miss}".format(acq=list(owned), - req=port_list, - miss=list(set(port_list).difference(owned))) - except KeyError as e: - cause = e.args[0] - print "Provided stream list name '{0}' doesn't exists.".format(cause) + if args: + removed_streams = self.streams_db.remove_stream_packs(args) + if removed_streams: + print green("The following stream packs were removed:") + print bold(", ".join(sorted(removed_streams))) + print format_text("[SUCCESS]\n", 'green', 'bold') + else: + print red("No streams were removed. Make sure to provide valid stream pack names.") else: - print "Please provide list name and ports to attach to, or leave empty to attach to all ports." + print magenta("Please provide stream pack name(s), separated with spaces.") + def do_stream_db_clear(self, line): + self.streams_db.clear() + print format_text("[SUCCESS]\n", 'green', 'bold') - def prompt_response(self, response_obj): - resp_list = response_obj if isinstance(response_obj, list) else [response_obj] - for response in resp_list: - print response + def complete_stream_db_remove(self, text, line, begidx, endidx): + return [x + for x in self.streams_db.get_loaded_streams_names() + if x.startswith(text)] + def do_attach(self, line): + args = line.split() + if len(args) >= 2: + stream_pack_name = args[0] + stream_list = self.streams_db.get_stream_pack(stream_pack_name) #user_streams[args[0]] + if not stream_list: + print "Provided stream list name '{0}' doesn't exists.".format(stream_pack_name) + print format_text("[FAILED]\n", 'red', 'bold') + return + if args[0] == "all": + ask = YesNoMenu('Are you sure you want to release all acquired ports ? ') + rc = ask.show() + if rc == False: + return + else: + port_list = self.stateless_client.get_acquired_ports() + else: + port_list = self.extract_port_ids_from_line(' '.join(args[1:])) + owned = set(self.stateless_client.get_owned_ports()) + if set(port_list).issubset(owned): + res_ok, log = self.stateless_client.add_stream_pack(port_list, stream_list.compiled) + # res_ok, msg = self.stateless_client.add_stream(port_list, stream_list.compiled) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + return + else: + print "Not all desired ports are acquired.\n" \ + "Acquired ports are: {acq}\n" \ + "Requested ports: {req}\n" \ + "Missing ports: {miss}".format(acq=list(owned), + req=port_list, + miss=list(set(port_list).difference(owned))) + print format_text("[FAILED]\n", 'red', 'bold') + else: + print "Please provide list name and ports to attach to, " \ + "or specify 'all' to attach all owned ports." + + def complete_attach(self, text, line, begidx, endidx): + arg_num = len(line.split()) - 1 + if arg_num == 1: + # return optional streams packs + return [x + for x in self.streams_db.get_loaded_streams_names() + if x.startswith(text)] + elif arg_num >= 2: + # return optional ports to attach to + return [x + for x in self.stateless_client.get_acquired_ports() + if str(x).startswith(text)] + else: + return [text] + def prompt_response(self, response_obj): + resp_list = response_obj if isinstance(response_obj, list) else [response_obj] + def format_return_status(return_status): + if return_status: + return green("OK") + else: + return green("fail") + for response in resp_list: + response_str = "{id:^3} - {msg} ({stat})".format(id=response.id, + msg=response.msg, + stat=format_return_status(response.success)) + print response_str + return + def do_remove_all_streams(self, line): + '''Acquire ports\n''' + # make sure that the user wants to acquire all + args = line.split() + if len(args) < 1: + print "Please provide a list of ports separated by spaces, " \ + "or specify 'all' to remove from all acquired ports" + if args[0] == "all": + ask = YesNoMenu('Are you sure you want to remove all stream packs from all acquired ports? ') + rc = ask.show() + if rc == False: + return + else: + port_list = self.stateless_client.get_port_ids() + else: + port_list = self.extract_port_ids_from_line(line) + print "\nTrying to remove all streams at ports: " + (" ".join(str(x) for x in port_list)) + "\n" + # rc, resp_list = self.stateless_client.take_ownership(port_list, force) + res_ok, log = self.stateless_client.remove_all_streams(port_list) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + return + def complete_remove_all_streams(self, text, line, begidx, endidx): + return [x + for x in self.stateless_client.get_acquired_ports() + if str(x).startswith(text)] # adds a very simple stream - def do_add_simple_stream (self, line): + def do_add_simple_stream(self, line): if line == "": add_stream = AddStreamMenu() add_stream.show() @@ -572,14 +684,14 @@ class TRexConsole(cmd.Cmd): packet = [0xFF,0xFF,0xFF] rc, msg = self.stateless_client.add_stream(port_id = port_id, stream_id = stream_id, isg = 1.1, next_stream_id = -1, packet = packet) if rc: - print "\nServer Response:\n\n" + self.stateless_client.pretty_json(json.dumps(msg)) + "\n" + print "\nServer Response:\n\n" + pretty_json(json.dumps(msg)) + "\n" else: print "\n*** " + msg + "\n" # aliasing do_exit = do_EOF = do_q = do_quit -def setParserOptions (): +def setParserOptions(): parser = argparse.ArgumentParser(prog="trex_console.py") parser.add_argument("-s", "--server", help = "TRex Server [default is localhost]", @@ -600,7 +712,7 @@ def setParserOptions (): return parser -def main (): +def main(): parser = setParserOptions() options = parser.parse_args()#sys.argv[1:]) -- cgit From 13c353b9e4f3f0177458c5bef729de31ec03135d Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 29 Oct 2015 03:59:43 +0200 Subject: fixed console issues after testsing them --- .../trex_control_plane/common/text_opts.py | 2 +- .../trex_control_plane/console/trex_console.py | 69 +++++++++++++++------- 2 files changed, 48 insertions(+), 23 deletions(-) mode change 100644 => 100755 scripts/automation/trex_control_plane/common/text_opts.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/common/text_opts.py b/scripts/automation/trex_control_plane/common/text_opts.py old mode 100644 new mode 100755 index 0d8323db..4d1cb0fd --- a/scripts/automation/trex_control_plane/common/text_opts.py +++ b/scripts/automation/trex_control_plane/common/text_opts.py @@ -66,7 +66,7 @@ def format_text(text, *args): func = FUNC_DICT.get(i) if func: return_string = func(return_string) - return + return return_string # pretty print for JSON def pretty_json (json_str, use_colors = True): diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index d2050f09..b2eac88e 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -84,10 +84,14 @@ class ConfirmMenu(object): def show(self): sys.stdout.write(self.caption) + input = user_input() + if input: + return False + else: + # user hit Enter + return True - - class CStreamsDB(object): def __init__(self): @@ -242,6 +246,9 @@ class TRexConsole(cmd.Cmd): self.do_acquire(line, True) + def complete_force_acquire(self, text, line, begidx, endidx): + return self.port_auto_complete(text, line, begidx, endidx, acquired=False) + def extract_port_ids_from_line(self, line): return {int(x) for x in line.split()} @@ -262,7 +269,7 @@ class TRexConsole(cmd.Cmd): return port_list - def do_acquire (self, line, force=False): + def do_acquire(self, line, force=False): '''Acquire ports\n''' # make sure that the user wants to acquire all @@ -270,7 +277,7 @@ class TRexConsole(cmd.Cmd): if len(args) < 1: print "Please provide a list of ports separated by spaces, or specify 'all' to acquire all available ports" if args[0] == "all": - ask = YesNoMenu('Are you sure you want to acquire all ports ? ') + ask = ConfirmMenu('Are you sure you want to acquire all ports ? ') rc = ask.show() if rc == False: return @@ -290,6 +297,23 @@ class TRexConsole(cmd.Cmd): print format_text("[SUCCESS]\n", 'green', 'bold') return + + def port_auto_complete(self, text, line, begidx, endidx, acquired=True): + if acquired: + ret_list = [x + for x in map(str, self.stateless_client.get_acquired_ports()) + if x.startswith(text)] + else: + ret_list = [x + for x in map(str, self.stateless_client.get_port_ids()) + if x.startswith(text)] + ret_list.append("all") + return ret_list + + + def complete_acquire(self, text, line, begidx, endidx): + return self.port_auto_complete(text, line, begidx, endidx, acquired=False) + def do_release (self, line): '''Release ports\n''' @@ -301,7 +325,7 @@ class TRexConsole(cmd.Cmd): if len(args) < 1: print "Please provide a list of ports separated by spaces, or specify 'all' to acquire all available ports" if args[0] == "all": - ask = YesNoMenu('Are you sure you want to release all acquired ports ? ') + ask = ConfirmMenu('Are you sure you want to release all acquired ports? ') rc = ask.show() if rc == False: return @@ -318,6 +342,9 @@ class TRexConsole(cmd.Cmd): print format_text("[SUCCESS]\n", 'green', 'bold') return + def complete_release(self, text, line, begidx, endidx): + return self.port_auto_complete(text, line, begidx, endidx) + def do_connect (self, line): '''Connects to the server\n''' @@ -332,9 +359,10 @@ class TRexConsole(cmd.Cmd): res_ok, msg = self.stateless_client.connect(sp[0], sp[1]) if res_ok: - print "[SUCCESS]\n" + print format_text("[SUCCESS]\n", 'green', 'bold') else: print "\n*** " + msg + "\n" + print format_text("[FAILED]\n", 'red', 'bold') return self.supported_rpc = self.stateless_client.get_supported_cmds().data @@ -477,9 +505,9 @@ class TRexConsole(cmd.Cmd): [StreamPack(v.stream_id, v.stream.dump()) for k, v in compiled_streams.items()])) if res_ok: - print green("Stream list '{0}' loaded and added successfully".format(name)) + print green("Stream pack '{0}' loaded and added successfully\n".format(name)) else: - print magenta("Picked name already exist. Please pick another name.") + print magenta("Picked name already exist. Please pick another name.\n") except Exception as e: print "adding new stream failed due to the following error:\n", str(e) print format_text("[FAILED]\n", 'red', 'bold') @@ -535,7 +563,7 @@ class TRexConsole(cmd.Cmd): except KeyError as e: print "Unknown stream list name provided" else: - print "\nAvailable stream lists:\n{0}".format(', '.join(sorted(self.streams_db.get_loaded_streams_names()))) + print "Available stream packs:\n{0}".format(', '.join(sorted(self.streams_db.get_loaded_streams_names()))) def complete_stream_db_show(self, text, line, begidx, endidx): return [x @@ -584,9 +612,9 @@ class TRexConsole(cmd.Cmd): port_list = self.stateless_client.get_acquired_ports() else: port_list = self.extract_port_ids_from_line(' '.join(args[1:])) - owned = set(self.stateless_client.get_owned_ports()) + owned = set(self.stateless_client.get_acquired_ports()) if set(port_list).issubset(owned): - res_ok, log = self.stateless_client.add_stream_pack(port_list, stream_list.compiled) + res_ok, log = self.stateless_client.add_stream_pack(port_list, *stream_list.compiled) # res_ok, msg = self.stateless_client.add_stream(port_list, stream_list.compiled) self.prompt_response(log) if not res_ok: @@ -615,9 +643,7 @@ class TRexConsole(cmd.Cmd): if x.startswith(text)] elif arg_num >= 2: # return optional ports to attach to - return [x - for x in self.stateless_client.get_acquired_ports() - if str(x).startswith(text)] + return self.port_auto_complete(text, line, begidx, endidx) else: return [text] @@ -627,7 +653,7 @@ class TRexConsole(cmd.Cmd): if return_status: return green("OK") else: - return green("fail") + return red("FAIL") for response in resp_list: response_str = "{id:^3} - {msg} ({stat})".format(id=response.id, @@ -642,15 +668,16 @@ class TRexConsole(cmd.Cmd): # make sure that the user wants to acquire all args = line.split() if len(args) < 1: - print "Please provide a list of ports separated by spaces, " \ - "or specify 'all' to remove from all acquired ports" + print magenta("Please provide a list of ports separated by spaces, " + "or specify 'all' to remove from all acquired ports") + return if args[0] == "all": - ask = YesNoMenu('Are you sure you want to remove all stream packs from all acquired ports? ') + ask = ConfirmMenu('Are you sure you want to remove all stream packs from all acquired ports? ') rc = ask.show() if rc == False: return else: - port_list = self.stateless_client.get_port_ids() + port_list = self.stateless_client.get_acquired_ports() else: port_list = self.extract_port_ids_from_line(line) @@ -666,9 +693,7 @@ class TRexConsole(cmd.Cmd): return def complete_remove_all_streams(self, text, line, begidx, endidx): - return [x - for x in self.stateless_client.get_acquired_ports() - if str(x).startswith(text)] + return self.port_auto_complete(text, line, begidx, endidx) # adds a very simple stream def do_add_simple_stream(self, line): -- cgit From d78150a66de591a77df2496e5de828d3232a931a Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 29 Oct 2015 06:18:54 +0200 Subject: Awesome working start/stop traffic console Fixed more stability issues :) Ready for merging. --- .../client/trex_stateless_client.py | 7 +- .../trex_control_plane/common/text_opts.py | 5 + .../trex_control_plane/console/trex_console.py | 162 ++++++++++++--------- 3 files changed, 105 insertions(+), 69 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 0fff9b36..4e861585 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -62,8 +62,8 @@ class CTRexStatelessClient(object): continue if bad_ids: # Some port IDs are not according to desires status - raise RuntimeError("The requested method ('{0}') cannot be invoked since port IDs {1} are not " - "at allowed stated".format(func.__name__, list(bad_ids))) + raise ValueError("The requested method ('{0}') cannot be invoked since port IDs {1} are not " + "at allowed states".format(func.__name__, list(bad_ids))) else: return func(self, *args, **kwargs) return wrapper_f @@ -127,6 +127,9 @@ class CTRexStatelessClient(object): def get_acquired_ports(self): return self._conn_handler.keys() + def get_active_ports(self): + return list(self._active_ports) + def acquire(self, port_id, force=False): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") diff --git a/scripts/automation/trex_control_plane/common/text_opts.py b/scripts/automation/trex_control_plane/common/text_opts.py index 4d1cb0fd..06c2c056 100755 --- a/scripts/automation/trex_control_plane/common/text_opts.py +++ b/scripts/automation/trex_control_plane/common/text_opts.py @@ -13,6 +13,8 @@ TEXT_CODES = {'bold': {'start': '\x1b[1m', 'end': '\x1b[39m'}, 'green': {'start': '\x1b[32m', 'end': '\x1b[39m'}, + 'yellow': {'start': '\x1b[33m', + 'end': '\x1b[39m'}, 'underline': {'start': '\x1b[4m', 'end': '\x1b[24m'}} @@ -40,6 +42,8 @@ def magenta(text): def green(text): return text_attribute(text, 'green') +def yellow(text): + return text_attribute(text, 'yellow') def underline(text): return text_attribute(text, 'underline') @@ -54,6 +58,7 @@ def text_attribute(text, attribute): FUNC_DICT = {'blue': blue, 'bold': bold, 'green': green, + 'yellow': yellow, 'cyan': cyan, 'magenta': magenta, 'underline': underline, diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index b2eac88e..5c3668c0 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -42,41 +42,6 @@ __version__ = "1.0" LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) -# - -def readch(choices=[]): - - fd = sys.stdin.fileno() - old_settings = termios.tcgetattr(fd) - try: - tty.setraw(sys.stdin.fileno()) - while True: - ch = sys.stdin.read(1) - if (ord(ch) == 3) or (ord(ch) == 4): - return None - if ch in choices: - return ch - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - - return None - -class YesNoMenu(object): - def __init__ (self, caption): - self.caption = caption - - def show (self): - print "{0}".format(self.caption) - sys.stdout.write("[Y/y/N/n] : ") - ch = readch(choices = ['y', 'Y', 'n', 'N']) - if ch == None: - return None - - print "\n" - if ch == 'y' or ch == 'Y': - return True - else: - return False class ConfirmMenu(object): def __init__ (self, caption): @@ -280,14 +245,13 @@ class TRexConsole(cmd.Cmd): ask = ConfirmMenu('Are you sure you want to acquire all ports ? ') rc = ask.show() if rc == False: + print yellow("[ABORTED]\n") return else: port_list = self.stateless_client.get_port_ids() else: port_list = self.extract_port_ids_from_line(line) - print "\nTrying to acquire ports: " + (" ".join(str(x) for x in port_list)) + "\n" - # rc, resp_list = self.stateless_client.take_ownership(port_list, force) res_ok, log = self.stateless_client.acquire(port_list, force) self.prompt_response(log) @@ -298,11 +262,16 @@ class TRexConsole(cmd.Cmd): return - def port_auto_complete(self, text, line, begidx, endidx, acquired=True): + def port_auto_complete(self, text, line, begidx, endidx, acquired=True, active=False): if acquired: - ret_list = [x - for x in map(str, self.stateless_client.get_acquired_ports()) - if x.startswith(text)] + if not active: + ret_list = [x + for x in map(str, self.stateless_client.get_acquired_ports()) + if x.startswith(text)] + else: + ret_list = [x + for x in map(str, self.stateless_client.get_active_ports()) + if x.startswith(text)] else: ret_list = [x for x in map(str, self.stateless_client.get_port_ids()) @@ -328,19 +297,24 @@ class TRexConsole(cmd.Cmd): ask = ConfirmMenu('Are you sure you want to release all acquired ports? ') rc = ask.show() if rc == False: + print yellow("[ABORTED]\n") return else: port_list = self.stateless_client.get_acquired_ports() else: port_list = self.extract_port_ids_from_line(line) - res_ok, log = self.stateless_client.release(port_list) - self.prompt_response(log) - if not res_ok: + try: + res_ok, log = self.stateless_client.release(port_list) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + except ValueError as e: + print magenta(str(e)) print format_text("[FAILED]\n", 'red', 'bold') return - print format_text("[SUCCESS]\n", 'green', 'bold') - return def complete_release(self, text, line, begidx, endidx): return self.port_auto_complete(text, line, begidx, endidx) @@ -489,7 +463,7 @@ class TRexConsole(cmd.Cmd): def do_stream_db_add(self, line): '''Loads a YAML stream list serialization into user console \n''' args = line.split() - if args >= 2: + if len(args) >= 2: name = args[0] yaml_path = args[1] try: @@ -512,22 +486,10 @@ class TRexConsole(cmd.Cmd): print "adding new stream failed due to the following error:\n", str(e) print format_text("[FAILED]\n", 'red', 'bold') - # if name in self.user_streams: - # print "Picked name already exist. Please pick another name." - # else: - # try: - # compiled_streams = stream_list.compile_streams() - # self.user_streams[name] = LoadedStreamList(loaded_obj, - # [StreamPack(v.stream_id, v.stream.dump()) - # for k, v in compiled_streams.items()]) - # - # print "Stream list '{0}' loaded successfully".format(name) - # except Exception as e: - # raise return else: - print "please provide load name and YAML path, separated by space.\n" \ - "Optionally, you may provide a third argument to specify multiplier." + print magenta("please provide load name and YAML path, separated by space.\n" + "Optionally, you may provide a third argument to specify multiplier.\n") @staticmethod def tree_autocomplete(text): @@ -573,7 +535,7 @@ class TRexConsole(cmd.Cmd): def do_stream_db_remove(self, line): args = line.split() if args: - removed_streams = self.streams_db.remove_stream_packs(args) + removed_streams = self.streams_db.remove_stream_packs(*args) if removed_streams: print green("The following stream packs were removed:") print bold(", ".join(sorted(removed_streams))) @@ -604,9 +566,10 @@ class TRexConsole(cmd.Cmd): print format_text("[FAILED]\n", 'red', 'bold') return if args[0] == "all": - ask = YesNoMenu('Are you sure you want to release all acquired ports ? ') + ask = ConfirmMenu('Are you sure you want to release all acquired ports? ') rc = ask.show() if rc == False: + print yellow("[ABORTED]\n") return else: port_list = self.stateless_client.get_acquired_ports() @@ -631,13 +594,15 @@ class TRexConsole(cmd.Cmd): miss=list(set(port_list).difference(owned))) print format_text("[FAILED]\n", 'red', 'bold') else: - print "Please provide list name and ports to attach to, " \ - "or specify 'all' to attach all owned ports." + print magenta("Please provide list name and ports to attach to, " + "or specify 'all' to attach all owned ports.\n") def complete_attach(self, text, line, begidx, endidx): arg_num = len(line.split()) - 1 if arg_num == 1: # return optional streams packs + if line.endswith(" "): + return self.port_auto_complete(text, line, begidx, endidx) return [x for x in self.streams_db.get_loaded_streams_names() if x.startswith(text)] @@ -675,14 +640,13 @@ class TRexConsole(cmd.Cmd): ask = ConfirmMenu('Are you sure you want to remove all stream packs from all acquired ports? ') rc = ask.show() if rc == False: + print yellow("[ABORTED]\n") return else: port_list = self.stateless_client.get_acquired_ports() else: port_list = self.extract_port_ids_from_line(line) - print "\nTrying to remove all streams at ports: " + (" ".join(str(x) for x in port_list)) + "\n" - # rc, resp_list = self.stateless_client.take_ownership(port_list, force) res_ok, log = self.stateless_client.remove_all_streams(port_list) self.prompt_response(log) @@ -695,6 +659,70 @@ class TRexConsole(cmd.Cmd): def complete_remove_all_streams(self, text, line, begidx, endidx): return self.port_auto_complete(text, line, begidx, endidx) + def do_start_traffic(self, line): + # make sure that the user wants to acquire all + args = line.split() + if len(args) < 1: + print magenta("Please provide a list of ports separated by spaces, " + "or specify 'all' to start traffic on all acquired ports") + return + if args[0] == "all": + ask = ConfirmMenu('Are you sure you want to start traffic at all acquired ports? ') + rc = ask.show() + if rc == False: + print yellow("[ABORTED]\n") + return + else: + port_list = self.stateless_client.get_acquired_ports() + else: + port_list = self.extract_port_ids_from_line(line) + + try: + res_ok, log = self.stateless_client.start_traffic(port_list) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + except ValueError as e: + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + + def complete_start_traffic(self, text, line, begidx, endidx): + return self.port_auto_complete(text, line, begidx, endidx) + + def do_stop_traffic(self, line): + # make sure that the user wants to acquire all + args = line.split() + if len(args) < 1: + print magenta("Please provide a list of ports separated by spaces, " + "or specify 'all' to stop traffic on all acquired ports") + return + if args[0] == "all": + ask = ConfirmMenu('Are you sure you want to start traffic at all acquired ports? ') + rc = ask.show() + if rc == False: + print yellow("[ABORTED]\n") + return + else: + port_list = self.stateless_client.get_active_ports() + else: + port_list = self.extract_port_ids_from_line(line) + + try: + res_ok, log = self.stateless_client.stop_traffic(port_list) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + except ValueError as e: + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + + def complete_stop_traffic(self, text, line, begidx, endidx): + return self.port_auto_complete(text, line, begidx, endidx, active=True) + # adds a very simple stream def do_add_simple_stream(self, line): if line == "": -- cgit From 2636c09cfb74c7981c27d84bcc72d00929fdbbbb Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 29 Oct 2015 07:14:58 +0200 Subject: Fixed verbose mode More stability fixes removed irrelevand methods --- .../client/trex_stateless_client.py | 10 ++ .../trex_control_plane/console/trex_console.py | 123 ++++++++++++--------- 2 files changed, 79 insertions(+), 54 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 4e861585..9e1c7cf3 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -31,6 +31,7 @@ class CTRexStatelessClient(object): super(CTRexStatelessClient, self).__init__() self.user = username self.comm_link = CTRexStatelessClient.CCommLink(server, port, virtual) + self.verbose = False self._conn_handler = {} self._active_ports = set() self._stats = CTRexStatsManager("port", "stream") @@ -130,6 +131,10 @@ class CTRexStatelessClient(object): def get_active_ports(self): return list(self._active_ports) + def set_verbose(self, mode): + self.comm_link.set_verbose(mode) + self.verbose = mode + def acquire(self, port_id, force=False): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") @@ -490,6 +495,7 @@ class CTRexStatelessClient(object): self.virtual = virtual self.server = server self.port = port + self.verbose = False self.rpc_link = JsonRpcClient(self.server, self.port) @property @@ -499,6 +505,10 @@ class CTRexStatelessClient(object): else: return True + def set_verbose(self, mode): + self.verbose = mode + return self.rpc_link.set_verbose(mode) + def connect(self): if not self.virtual: return self.rpc_link.connect() diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 5c3668c0..51a1f8cc 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -43,6 +43,23 @@ __version__ = "1.0" LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) +def readch(choices=[]): + + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + while True: + ch = sys.stdin.read(1) + if (ord(ch) == 3) or (ord(ch) == 4): + return None + if ch in choices: + return ch + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + + return None + class ConfirmMenu(object): def __init__ (self, caption): self.caption = "{cap} [confirm] : ".format(cap=caption) @@ -169,30 +186,31 @@ class TRexConsole(cmd.Cmd): elif line == "on": self.verbose = True self.stateless_client.set_verbose(True) - print "\nverbose set to on\n" + print green("\nverbose set to on\n") elif line == "off": self.verbose = False self.stateless_client.set_verbose(False) - print "\nverbose set to off\n" + print green("\nverbose set to off\n") else: - print "\nplease specify 'on' or 'off'\n" + print magenta("\nplease specify 'on' or 'off'\n") # query the server for registered commands def do_query_server(self, line): '''query the RPC server for supported remote commands\n''' - rc, msg = self.stateless_client.query_rpc_server() - if not rc: - print "\n*** " + msg + "\n" + res_ok, msg = self.stateless_client.get_supported_cmds() + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') return - - print "\nRPC server supports the following commands: \n\n" + print "\nRPC server supports the following commands:\n" for func in msg: if func: print func - print "\n" + print '' + print format_text("[SUCCESS]\n", 'green', 'bold') + return def do_ping (self, line): '''Pings the RPC server\n''' @@ -253,13 +271,16 @@ class TRexConsole(cmd.Cmd): port_list = self.extract_port_ids_from_line(line) # rc, resp_list = self.stateless_client.take_ownership(port_list, force) - res_ok, log = self.stateless_client.acquire(port_list, force) - self.prompt_response(log) - if not res_ok: + try: + res_ok, log = self.stateless_client.acquire(port_list, force) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + except ValueError as e: + print magenta(str(e)) print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - return def port_auto_complete(self, text, line, begidx, endidx, acquired=True, active=False): @@ -533,6 +554,7 @@ class TRexConsole(cmd.Cmd): if x.startswith(text)] def do_stream_db_remove(self, line): + '''Removes a single loaded stream packs from loaded stream pack repository\n''' args = line.split() if args: removed_streams = self.streams_db.remove_stream_packs(*args) @@ -546,6 +568,7 @@ class TRexConsole(cmd.Cmd): print magenta("Please provide stream pack name(s), separated with spaces.") def do_stream_db_clear(self, line): + '''Clears all loaded stream packs from loaded stream pack repository\n''' self.streams_db.clear() print format_text("[SUCCESS]\n", 'green', 'bold') @@ -557,6 +580,7 @@ class TRexConsole(cmd.Cmd): def do_attach(self, line): + '''Assign loaded stream pack into specified ports on TRex\n''' args = line.split() if len(args) >= 2: stream_pack_name = args[0] @@ -576,22 +600,26 @@ class TRexConsole(cmd.Cmd): else: port_list = self.extract_port_ids_from_line(' '.join(args[1:])) owned = set(self.stateless_client.get_acquired_ports()) - if set(port_list).issubset(owned): - res_ok, log = self.stateless_client.add_stream_pack(port_list, *stream_list.compiled) - # res_ok, msg = self.stateless_client.add_stream(port_list, stream_list.compiled) - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') + try: + if set(port_list).issubset(owned): + res_ok, log = self.stateless_client.add_stream_pack(port_list, *stream_list.compiled) + # res_ok, msg = self.stateless_client.add_stream(port_list, stream_list.compiled) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') return - print format_text("[SUCCESS]\n", 'green', 'bold') - return - else: - print "Not all desired ports are acquired.\n" \ - "Acquired ports are: {acq}\n" \ - "Requested ports: {req}\n" \ - "Missing ports: {miss}".format(acq=list(owned), - req=port_list, - miss=list(set(port_list).difference(owned))) + else: + print "Not all desired ports are acquired.\n" \ + "Acquired ports are: {acq}\n" \ + "Requested ports: {req}\n" \ + "Missing ports: {miss}".format(acq=list(owned), + req=port_list, + miss=list(set(port_list).difference(owned))) + print format_text("[FAILED]\n", 'red', 'bold') + except ValueError as e: + print magenta(str(e)) print format_text("[FAILED]\n", 'red', 'bold') else: print magenta("Please provide list name and ports to attach to, " @@ -648,18 +676,22 @@ class TRexConsole(cmd.Cmd): port_list = self.extract_port_ids_from_line(line) # rc, resp_list = self.stateless_client.take_ownership(port_list, force) - res_ok, log = self.stateless_client.remove_all_streams(port_list) - self.prompt_response(log) - if not res_ok: + try: + res_ok, log = self.stateless_client.remove_all_streams(port_list) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + except ValueError as e: + print magenta(str(e)) print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - return def complete_remove_all_streams(self, text, line, begidx, endidx): return self.port_auto_complete(text, line, begidx, endidx) def do_start_traffic(self, line): + '''Start pre-submitted traffic in specified ports on TRex\n''' # make sure that the user wants to acquire all args = line.split() if len(args) < 1: @@ -692,6 +724,7 @@ class TRexConsole(cmd.Cmd): return self.port_auto_complete(text, line, begidx, endidx) def do_stop_traffic(self, line): + '''Stop active traffic in specified ports on TRex\n''' # make sure that the user wants to acquire all args = line.split() if len(args) < 1: @@ -723,24 +756,6 @@ class TRexConsole(cmd.Cmd): def complete_stop_traffic(self, text, line, begidx, endidx): return self.port_auto_complete(text, line, begidx, endidx, active=True) - # adds a very simple stream - def do_add_simple_stream(self, line): - if line == "": - add_stream = AddStreamMenu() - add_stream.show() - return - - params = line.split() - port_id = int(params[0]) - stream_id = int(params[1]) - - packet = [0xFF,0xFF,0xFF] - rc, msg = self.stateless_client.add_stream(port_id = port_id, stream_id = stream_id, isg = 1.1, next_stream_id = -1, packet = packet) - if rc: - print "\nServer Response:\n\n" + pretty_json(json.dumps(msg)) + "\n" - else: - print "\n*** " + msg + "\n" - # aliasing do_exit = do_EOF = do_q = do_quit -- cgit From eacf2829c309011bf15d56b7b531b22ebeaf4d7d Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 1 Nov 2015 17:29:07 +0200 Subject: added support for 'mul' field in start_traffic RPC command --- .../automation/trex_control_plane/client/trex_stateless_client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 9e1c7cf3..8231fe33 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -264,7 +264,7 @@ class CTRexStatelessClient(object): if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications - commands = [RpcCmdData("start_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + commands = [RpcCmdData("start_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id, "mul": 1.0}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) if rc: @@ -272,7 +272,8 @@ class CTRexStatelessClient(object): success_test=self.ack_success_test) else: params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id} + "port_id": port_id, + "mul": 1.0} command = RpcCmdData("start_traffic", params) return self._handle_start_traffic_response(command, self.transmit(command.method, command.params), -- cgit From 7d7767e17b1a4e54a8934ded724f54dc5b6228ce Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 1 Nov 2015 18:03:17 +0200 Subject: added support for a new RPC command : sync_user provides a way to sync a console / GUI to the server for a specific user --- scripts/automation/trex_control_plane/console/trex_console.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 51a1f8cc..ec23eb0c 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -393,9 +393,9 @@ class TRexConsole(cmd.Cmd): print "Example: rpc test_add {'x': 12, 'y': 17}\n" return - res_ok, msg = self.stateless_client.invoke_rpc_method(method, params) + res_ok, msg = self.stateless_client.transmit(method, params) if res_ok: - print "\nServer Response:\n\n" + self.stateless_client.pretty_json(json.dumps(msg)) + "\n" + print "\nServer Response:\n\n" + pretty_json(json.dumps(msg)) + "\n" else: print "\n*** " + msg + "\n" #print "Please try 'reconnect' to reconnect to server" -- cgit From 1586ab131f28c03ea65373d9e702e4051ffb9a56 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 2 Nov 2015 16:14:02 +0200 Subject: status is back online + ZMQ async stats --- .../trex_control_plane/client/trex_async_client.py | 172 +++++++++++++ .../client/trex_stateless_client.py | 23 +- .../trex_control_plane/console/trex_console.py | 11 +- .../trex_control_plane/console/trex_status.py | 284 ++++++++++----------- 4 files changed, 335 insertions(+), 155 deletions(-) create mode 100644 scripts/automation/trex_control_plane/client/trex_async_client.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py new file mode 100644 index 00000000..ea716057 --- /dev/null +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -0,0 +1,172 @@ +#!/router/bin/python + +try: + # support import for Python 2 + import outer_packages +except ImportError: + # support import for Python 3 + import client.outer_packages +from client_utils.jsonrpc_client import JsonRpcClient, BatchMessage + +import json +import threading +import time +import zmq +import re + +from common.trex_stats import * +from common.trex_streams import * + +# basic async stats class +class TrexAsyncStats(object): + def __init__ (self): + self.ref_point = None + + def update (self, snapshot): + + self.current = snapshot + + if self.ref_point == None: + self.ref_point = self.current + + + def get (self, field): + + if not field in self.current: + return None + + return self.current[field] + + def get_rel (self, field): + if not field in self.current: + return None + + return self.current[field] - self.ref_point[field] + + +# describes the general stats provided by TRex +class TrexAsyncStatsGeneral(TrexAsyncStats): + def __init__ (self): + super(TrexAsyncStatsGeneral, self).__init__() + + +# per port stats +class TrexAsyncStatsPort(TrexAsyncStats): + def __init__ (self): + super(TrexAsyncStatsPort, self).__init__() + + +# stats manager +class TrexAsyncStatsManager(): + def __init__ (self, port_count): + self.port_count = port_count + + self.general_stats = TrexAsyncStatsGeneral() + self.port_stats = {} + + def get_general_stats (self): + return self.general_stats + + def get_port_stats (self, port_id): + + if not port_id in self.port_stats: + return None + + return self.port_stats[port_id] + + + def update (self, snapshot): + + if snapshot['name'] == 'trex-global': + self.__handle_snapshot(snapshot['data']) + else: + # for now ignore the rest + return + + def __handle_snapshot (self, snapshot): + + general_stats = {} + port_stats = {} + + # filter the values per port and general + for key, value in snapshot.iteritems(): + + # match a pattern of ports + m = re.search('.*\-([0-8])', key) + if m: + port_id = m.group(1) + + if not port_id in port_stats: + port_stats[port_id] = {} + + port_stats[port_id][key] = value + + else: + # no port match - general stats + general_stats[key] = value + + # update the general object with the snapshot + self.general_stats.update(general_stats) + + # update all ports + for port_id, data in port_stats.iteritems(): + + if not port_id in self.port_stats: + self.port_stats[port_id] = TrexAsyncStatsPort() + + self.port_stats[port_id].update(data) + + + + + +class TrexAsyncClient(): + def __init__ (self, port): + + self.port = port + + self.raw_snapshot = {} + + self.stats = TrexAsyncStatsManager(1) + + self.active = True + self.t = threading.Thread(target = self._run) + + # kill this thread on exit and don't add it to the join list + self.t.setDaemon(True) + self.t.start() + + + def _run (self): + + # Socket to talk to server + self.context = zmq.Context() + self.socket = self.context.socket(zmq.SUB) + + self.c = "tcp://localhost:{0}".format(self.port) + print "Connecting To ZMQ Publisher At {0}".format(self.c) + + self.socket.connect(self.c) + self.socket.setsockopt(zmq.SUBSCRIBE, '') + + while self.active: + msg = json.loads(self.socket.recv_string()) + + key = msg['name'] + self.raw_snapshot[key] = msg['data'] + + self.stats.update(msg) + + + def get_stats (self): + return self.stats + + def get_raw_snapshot (self): + #return str(self.stats.global_stats.get('m_total_tx_bytes')) + " / " + str(self.stats.global_stats.get_rel('m_total_tx_bytes')) + return self.raw_snapshot + + + def stop (self): + self.active = False + self.t.join() + diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 8231fe33..c180e0d1 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -13,6 +13,8 @@ from common.trex_stats import * from common.trex_streams import * from collections import namedtuple +from trex_async_client import TrexAsyncClient + RpcCmdData = namedtuple('RpcCmdData', ['method', 'params']) class RpcResponseStatus(namedtuple('RpcResponseStatus', ['success', 'id', 'msg'])): @@ -27,10 +29,10 @@ class RpcResponseStatus(namedtuple('RpcResponseStatus', ['success', 'id', 'msg'] class CTRexStatelessClient(object): """docstring for CTRexStatelessClient""" - def __init__(self, username, server="localhost", port=5050, virtual=False): + def __init__(self, username, server="localhost", sync_port=5050, async_port = 4500, virtual=False): super(CTRexStatelessClient, self).__init__() self.user = username - self.comm_link = CTRexStatelessClient.CCommLink(server, port, virtual) + self.comm_link = CTRexStatelessClient.CCommLink(server, sync_port, virtual) self.verbose = False self._conn_handler = {} self._active_ports = set() @@ -39,6 +41,9 @@ class CTRexStatelessClient(object): self._server_version = None self.__err_log = None + self._async_client = TrexAsyncClient(async_port) + + # ----- decorator methods ----- # def force_status(owned=True, active_and_owned=False): def wrapper(func): @@ -100,6 +105,12 @@ class CTRexStatelessClient(object): return rc, err return self._init_sync() + def get_stats_async (self): + return self._async_client.get_stats() + + def get_connection_port (self): + return self.comm_link.port + def disconnect(self): return self.comm_link.disconnect() @@ -300,10 +311,10 @@ class CTRexStatelessClient(object): self.transmit(command.method, command.params), self.ack_success_test) - def get_global_stats(self): - command = RpcCmdData("get_global_stats", {}) - return self._handle_get_global_stats_response(command, self.transmit(command.method, command.params)) - # return self.transmit("get_global_stats") +# def get_global_stats(self): +# command = RpcCmdData("get_global_stats", {}) +# return self._handle_get_global_stats_response(command, self.transmit(command.method, command.params)) +# # return self.transmit("get_global_stats") @force_status(owned=True, active_and_owned=True) def get_port_stats(self, port_id=None): diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index ec23eb0c..bd79cb42 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -34,7 +34,6 @@ from common.text_opts import * from client_utils.general_utils import user_input -from client_utils.jsonrpc_client import TrexStatelessClient import trex_status from collections import namedtuple @@ -177,6 +176,7 @@ class TRexConsole(cmd.Cmd): dotext = 'do_'+text return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)] + # set verbose on / off def do_verbose (self, line): '''Shows or set verbose mode\n''' @@ -770,6 +770,10 @@ def setParserOptions(): default = 5050, type = int) + parser.add_argument("-z", "--pub", help = "TRex Async Publisher Port [default is 4500]\n", + default = 4500, + type = int) + parser.add_argument("-u", "--user", help = "User Name [default is random generated]\n", default = 'user_' + ''.join(random.choice(string.digits) for _ in range(5)), type = str) @@ -782,11 +786,10 @@ def setParserOptions(): def main(): parser = setParserOptions() - options = parser.parse_args()#sys.argv[1:]) + options = parser.parse_args() # Stateless client connection - # stateless_client = TrexStatelessClient(options.server, options.port, options.user) - stateless_client = CTRexStatelessClient(options.user, options.server, options.port) + stateless_client = CTRexStatelessClient(options.user, options.server, options.port, options.pub) # console try: diff --git a/scripts/automation/trex_control_plane/console/trex_status.py b/scripts/automation/trex_control_plane/console/trex_status.py index 2c5a648f..4cd07358 100644 --- a/scripts/automation/trex_control_plane/console/trex_status.py +++ b/scripts/automation/trex_control_plane/console/trex_status.py @@ -11,6 +11,8 @@ import datetime g_curses_active = False +################### utils ################# + # simple percetange show def percentage (a, total): x = int ((float(a) / total) * 100) @@ -18,15 +20,25 @@ def percentage (a, total): # simple float to human readable def float_to_human_readable (size, suffix = "bps"): - for unit in ['','K','M','G']: - if abs(size) < 1024.0: - return "%3.1f %s%s" % (size, unit, suffix) - size /= 1024.0 + for unit in ['','K','M','G','T']: + if abs(size) < 1000.0: + return "%3.2f %s%s" % (size, unit, suffix) + size /= 1000.0 return "NaN" + +################### panels ################# + # panel object class TrexStatusPanel(object): - def __init__ (self, h, l, y, x, headline): + def __init__ (self, h, l, y, x, headline, status_obj): + + self.status_obj = status_obj + + self.log = status_obj.log + self.stateless_client = status_obj.stateless_client + self.general_stats = status_obj.general_stats + self.h = h self.l = l self.y = y @@ -53,64 +65,26 @@ class TrexStatusPanel(object): return self.win -# total stats (ports + global) -class Stats(): - def __init__ (self, rpc_client, port_list, interval = 100): - - self.rpc_client = rpc_client - - self.port_list = port_list - self.port_stats = {} - - self.interval = interval - self.delay_count = 0 - - def get_port_stats (self, port_id): - if self.port_stats.get(port_id): - return self.port_stats[port_id] - else: - return None - - def query_sync (self): - self.delay_count += 1 - if self.delay_count < self.interval: - return - - self.delay_count = 0 - - # query global stats - - # query port stats - - rc, resp_list = self.rpc_client.get_port_stats(self.port_list) - if not rc: - return - - for i, rc in enumerate(resp_list): - if rc[0]: - self.port_stats[self.port_list[i]] = rc[1] - - # various kinds of panels # Server Info Panel class ServerInfoPanel(TrexStatusPanel): def __init__ (self, h, l, y, x, status_obj): - super(ServerInfoPanel, self).__init__(h, l, y ,x ,"Server Info:") - - self.status_obj = status_obj + super(ServerInfoPanel, self).__init__(h, l, y ,x ,"Server Info:", status_obj) def draw (self): - if self.status_obj.server_version == None: + if not self.status_obj.server_version : return - self.clear() + if not self.status_obj.server_sys_info: + return - connection_details = self.status_obj.rpc_client.get_connection_details() - self.getwin().addstr(3, 2, "{:<30} {:30}".format("Server:",self.status_obj.server_sys_info["hostname"] + ":" + str(connection_details['port']))) + self.clear() + + self.getwin().addstr(3, 2, "{:<30} {:30}".format("Server:",self.status_obj.server_sys_info["hostname"] + ":" + str(self.stateless_client.get_connection_port()))) self.getwin().addstr(4, 2, "{:<30} {:30}".format("Version:", self.status_obj.server_version["version"])) self.getwin().addstr(5, 2, "{:<30} {:30}".format("Build:", self.status_obj.server_version["build_date"] + " @ " + @@ -123,7 +97,7 @@ class ServerInfoPanel(TrexStatusPanel): self.getwin().addstr(9, 2, "{:<30} {:<30}".format("Ports Count:", self.status_obj.server_sys_info["port_count"])) - ports_owned = " ".join(str(x) for x in self.status_obj.rpc_client.get_owned_ports()) + ports_owned = " ".join(str(x) for x in self.status_obj.owned_ports) if not ports_owned: ports_owned = "None" @@ -134,26 +108,39 @@ class ServerInfoPanel(TrexStatusPanel): class GeneralInfoPanel(TrexStatusPanel): def __init__ (self, h, l, y, x, status_obj): - super(GeneralInfoPanel, self).__init__(h, l, y ,x ,"General Info:") - - self.status_obj = status_obj + super(GeneralInfoPanel, self).__init__(h, l, y ,x ,"General Info:", status_obj) def draw (self): - pass + self.clear() + + self.getwin().addstr(3, 2, "{:<30} {:0.2f} %".format("CPU util.:", self.general_stats.get("m_cpu_util"))) + + self.getwin().addstr(5, 2, "{:<30} {:} / {:}".format("Total Tx. rate:", + float_to_human_readable(self.general_stats.get("m_tx_bps")), + float_to_human_readable(self.general_stats.get("m_tx_pps"), suffix = "pps"))) + + # missing RX field + #self.getwin().addstr(5, 2, "{:<30} {:} / {:}".format("Total Rx. rate:", + # float_to_human_readable(self.general_stats.get("m_rx_bps")), + # float_to_human_readable(self.general_stats.get("m_rx_pps"), suffix = "pps"))) + + self.getwin().addstr(7, 2, "{:<30} {:} / {:}".format("Total Tx:", + float_to_human_readable(self.general_stats.get_rel("m_total_tx_bytes"), suffix = "B"), + float_to_human_readable(self.general_stats.get_rel("m_total_tx_pkts"), suffix = "pkts"))) # all ports stats class PortsStatsPanel(TrexStatusPanel): def __init__ (self, h, l, y, x, status_obj): - super(PortsStatsPanel, self).__init__(h, l, y ,x ,"Trex Ports:") + super(PortsStatsPanel, self).__init__(h, l, y ,x ,"Trex Ports:", status_obj) - self.status_obj = status_obj def draw (self): self.clear() + return - owned_ports = self.status_obj.rpc_client.get_owned_ports() + owned_ports = self.status_obj.owned_ports if not owned_ports: self.getwin().addstr(3, 2, "No Owned Ports - Please Acquire One Or More Ports") return @@ -193,33 +180,23 @@ class PortsStatsPanel(TrexStatusPanel): class ControlPanel(TrexStatusPanel): def __init__ (self, h, l, y, x, status_obj): - super(ControlPanel, self).__init__(h, l, y, x, "") + super(ControlPanel, self).__init__(h, l, y, x, "", status_obj) - self.status_obj = status_obj def draw (self): self.clear() self.getwin().addstr(1, 2, "'g' - general, '0-{0}' - specific port, 'f' - freeze, 'c' - clear stats, 'p' - ping server, 'q' - quit" - .format(self.status_obj.rpc_client.get_port_count() - 1)) + .format(self.status_obj.stateless_client.get_port_count() - 1)) - index = 3 - - cut = len(self.status_obj.log) - 4 - if cut < 0: - cut = 0 - - for l in self.status_obj.log[cut:]: - self.getwin().addstr(index, 2, l) - index += 1 + self.log.draw(self.getwin(), 2, 3) # specific ports panels class SinglePortPanel(TrexStatusPanel): def __init__ (self, h, l, y, x, status_obj, port_id): - super(SinglePortPanel, self).__init__(h, l, y, x, "Port {0}".format(port_id)) + super(SinglePortPanel, self).__init__(h, l, y, x, "Port {0}".format(port_id), status_obj) - self.status_obj = status_obj self.port_id = port_id def draw (self): @@ -227,7 +204,7 @@ class SinglePortPanel(TrexStatusPanel): self.clear() - if not self.port_id in self.status_obj.rpc_client.get_owned_ports(): + if not self.port_id in self.status_obj.stateless_client.get_owned_ports(): self.getwin().addstr(y, 2, "Port {0} is not owned by you, please acquire the port for more info".format(self.port_id)) return @@ -292,96 +269,119 @@ class SinglePortPanel(TrexStatusPanel): y += 2 -# status object -class TrexStatus(): - def __init__ (self, stdscr, rpc_client): - self.stdscr = stdscr +################### main objects ################# + +# status log +class TrexStatusLog(): + def __init__ (self): self.log = [] - self.rpc_client = rpc_client - self.snapshot = self.rpc_client.snapshot() + def add_event (self, msg): + self.log.append("[{0}] {1}".format(str(datetime.datetime.now().time()), msg)) - # fetch server info - self.get_server_info() + def draw (self, window, x, y, max_lines = 4): + index = y + + cut = len(self.log) - max_lines + if cut < 0: + cut = 0 + + for msg in self.log[cut:]: + window.addstr(index, x, msg) + index += 1 + +# status commands +class TrexStatusCommands(): + def __init__ (self, status_object): + + self.status_object = status_object - # create stats objects - self.stats = Stats(rpc_client, self.rpc_client.get_owned_ports()) + self.stateless_client = status_object.stateless_client + self.log = self.status_object.log - # register actions self.actions = {} - self.actions[ord('q')] = self.action_quit - self.actions[ord('p')] = self.action_ping - self.actions[ord('f')] = self.action_freeze + self.actions[ord('q')] = self._quit + self.actions[ord('p')] = self._ping + self.actions[ord('f')] = self._freeze - self.actions[ord('g')] = self.action_show_ports_stats + self.actions[ord('g')] = self._show_ports_stats - for port_id in xrange(0, self.rpc_client.get_port_count()): - self.actions[ord('0') + port_id] = self.action_show_port_generator(port_id) + # register all the available ports shortcuts + for port_id in xrange(0, self.stateless_client.get_port_count()): + self.actions[ord('0') + port_id] = self._show_port_generator(port_id) + + + # handle a key pressed + def handle (self, ch): + if ch in self.actions: + return self.actions[ch]() + else: + self.log.add_event("Unknown key pressed, please see legend") + return True + + # show all ports + def _show_ports_stats (self): + self.log.add_event("Switching to all ports view") + self.status_object.stats_panel = self.status_object.ports_stats_panel - - # all ports stats - def action_show_ports_stats (self): - self.add_log_event("Switching to all ports view") - self.stats_panel = self.ports_stats_panel - return True - # function generator for different ports requests - def action_show_port_generator (self, port_id): - def action_show_port(): - self.add_log_event("Switching panel to port {0}".format(port_id)) - self.stats_panel = self.ports_panels[port_id] + + # function generator for different ports requests + def _show_port_generator (self, port_id): + def _show_port(): + self.log.add_event("Switching panel to port {0}".format(port_id)) + self.status_object.stats_panel = self.status_object.ports_panels[port_id] return True - return action_show_port + return _show_port - def action_freeze (self): - self.update_active = not self.update_active - self.add_log_event("Update continued" if self.update_active else "Update stopped") + def _freeze (self): + self.status_object.update_active = not self.status_object.update_active + self.log.add_event("Update continued" if self.status_object.update_active else "Update stopped") return True - def action_quit(self): + def _quit(self): return False - def action_ping (self): - self.add_log_event("Pinging RPC server") + def _ping (self): + self.log.add_event("Pinging RPC server") - rc, msg = self.rpc_client.ping_rpc_server() + rc, msg = self.stateless_client.ping() if rc: - self.add_log_event("Server replied: '{0}'".format(msg)) + self.log.add_event("Server replied: '{0}'".format(msg)) else: - self.add_log_event("Failed to get reply") + self.log.add_event("Failed to get reply") return True - def get_server_info (self): - - self.server_version = self.rpc_client.get_rpc_server_version() - self.server_sys_info = self.rpc_client.get_system_info() - - - def add_log_event (self, msg): - self.log.append("[{0}] {1}".format(str(datetime.datetime.now().time()), msg)) - - # control panel - def update_control (self): - self.control_panel.clear() +# status object +# +# +# +class TrexStatus(): + def __init__ (self, stdscr, stateless_client): + self.stdscr = stdscr - self.control_panel.getwin().addstr(1, 2, "'g' - general, '0-{0}' - specific port, 'f' - freeze, 'c' - clear stats, 'p' - ping server, 'q' - quit" - .format(self.rpc_client.get_port_count() - 1)) + self.stateless_client = stateless_client + self.general_stats = stateless_client.get_stats_async().get_general_stats() - index = 3 + # fetch server info + rc, self.server_sys_info = self.stateless_client.get_system_info() + if not rc: + return - cut = len(self.log) - 4 - if cut < 0: - cut = 0 + rc, self.server_version = self.stateless_client.get_version() + if not rc: + return - for l in self.log[cut:]: - self.control_panel.getwin().addstr(index, 2, l) - index += 1 + self.owned_ports = self.stateless_client.get_acquired_ports() + self.log = TrexStatusLog() + self.cmds = TrexStatusCommands(self) + def generate_layout (self): self.max_y = self.stdscr.getmaxyx()[0] self.max_x = self.stdscr.getmaxyx()[1] @@ -394,7 +394,7 @@ class TrexStatus(): self.ports_stats_panel = PortsStatsPanel(int(self.max_y * 0.8), self.max_x / 2, 0, 0, self) self.ports_panels = {} - for i in xrange(0, self.rpc_client.get_port_count()): + for i in xrange(0, self.stateless_client.get_port_count()): self.ports_panels[i] = SinglePortPanel(int(self.max_y * 0.8), self.max_x / 2, 0, 0, self, i) # at start time we point to the main one @@ -411,14 +411,8 @@ class TrexStatus(): # no key , continue if ch == curses.ERR: return True - - # check for registered function - if ch in self.actions: - return self.actions[ch]() - else: - self.add_log_event("Unknown key pressed, please see legend") - - return True + + return self.cmds.handle(ch) # main run entry point def run (self): @@ -454,14 +448,14 @@ class TrexStatus(): sleep(0.01) -def show_trex_status_internal (stdscr, rpc_client): - trex_status = TrexStatus(stdscr, rpc_client) +def show_trex_status_internal (stdscr, stateless_client): + trex_status = TrexStatus(stdscr, stateless_client) trex_status.run() -def show_trex_status (rpc_client): +def show_trex_status (stateless_client): try: - curses.wrapper(show_trex_status_internal, rpc_client) + curses.wrapper(show_trex_status_internal, stateless_client) except KeyboardInterrupt: curses.endwin() -- cgit From 3fb4e4c130da10e58af07e1f783f093515e90f96 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 2 Nov 2015 16:40:34 +0200 Subject: few bug fixes for last commit --- .../trex_control_plane/client/trex_async_client.py | 30 +++++++++++++++------- .../trex_control_plane/console/trex_console.py | 4 +++ .../trex_control_plane/console/trex_status.py | 11 ++++++-- 3 files changed, 34 insertions(+), 11 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index ea716057..1ce10288 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -11,6 +11,7 @@ from client_utils.jsonrpc_client import JsonRpcClient, BatchMessage import json import threading import time +import datetime import zmq import re @@ -21,9 +22,14 @@ from common.trex_streams import * class TrexAsyncStats(object): def __init__ (self): self.ref_point = None + self.current = {} + self.last_update_ts = datetime.datetime.now() def update (self, snapshot): + #update + self.last_update_ts = datetime.datetime.now() + self.current = snapshot if self.ref_point == None: @@ -33,17 +39,22 @@ class TrexAsyncStats(object): def get (self, field): if not field in self.current: - return None + return 0 return self.current[field] def get_rel (self, field): if not field in self.current: - return None + return 0 return self.current[field] - self.ref_point[field] + # return true if new data has arrived in the past 2 seconds + def is_online (self): + delta_ms = (datetime.datetime.now() - self.last_update_ts).total_seconds() * 1000 + return (delta_ms < 2000) + # describes the general stats provided by TRex class TrexAsyncStatsGeneral(TrexAsyncStats): def __init__ (self): @@ -58,12 +69,12 @@ class TrexAsyncStatsPort(TrexAsyncStats): # stats manager class TrexAsyncStatsManager(): - def __init__ (self, port_count): - self.port_count = port_count + def __init__ (self): self.general_stats = TrexAsyncStatsGeneral() self.port_stats = {} + def get_general_stats (self): return self.general_stats @@ -127,7 +138,11 @@ class TrexAsyncClient(): self.raw_snapshot = {} - self.stats = TrexAsyncStatsManager(1) + self.stats = TrexAsyncStatsManager() + + + self.tr = "tcp://localhost:{0}".format(self.port) + print "\nConnecting To ZMQ Publisher At {0}".format(self.tr) self.active = True self.t = threading.Thread(target = self._run) @@ -143,10 +158,7 @@ class TrexAsyncClient(): self.context = zmq.Context() self.socket = self.context.socket(zmq.SUB) - self.c = "tcp://localhost:{0}".format(self.port) - print "Connecting To ZMQ Publisher At {0}".format(self.c) - - self.socket.connect(self.c) + self.socket.connect(self.tr) self.socket.setsockopt(zmq.SUBSCRIBE, '') while self.active: diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index bd79cb42..549262c5 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -409,6 +409,10 @@ class TRexConsole(cmd.Cmd): def do_status (self, line): '''Shows a graphical console\n''' + if not self.stateless_client.is_connected(): + print "Not connected to server\n" + return + self.do_verbose('off') trex_status.show_trex_status(self.stateless_client) diff --git a/scripts/automation/trex_control_plane/console/trex_status.py b/scripts/automation/trex_control_plane/console/trex_status.py index 4cd07358..4e73e0bb 100644 --- a/scripts/automation/trex_control_plane/console/trex_status.py +++ b/scripts/automation/trex_control_plane/console/trex_status.py @@ -113,6 +113,10 @@ class GeneralInfoPanel(TrexStatusPanel): def draw (self): self.clear() + if not self.general_stats.is_online(): + self.getwin().addstr(3, 2, "No Published Data From TRex Server") + return + self.getwin().addstr(3, 2, "{:<30} {:0.2f} %".format("CPU util.:", self.general_stats.get("m_cpu_util"))) self.getwin().addstr(5, 2, "{:<30} {:} / {:}".format("Total Tx. rate:", @@ -366,6 +370,10 @@ class TrexStatus(): self.stdscr = stdscr self.stateless_client = stateless_client + + self.log = TrexStatusLog() + self.cmds = TrexStatusCommands(self) + self.general_stats = stateless_client.get_stats_async().get_general_stats() # fetch server info @@ -379,8 +387,7 @@ class TrexStatus(): self.owned_ports = self.stateless_client.get_acquired_ports() - self.log = TrexStatusLog() - self.cmds = TrexStatusCommands(self) + def generate_layout (self): self.max_y = self.stdscr.getmaxyx()[0] -- cgit From 274bca264036d9cf01b9fcbbb3923b0f28654d82 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 3 Nov 2015 09:36:59 +0200 Subject: Added parsing file for console advanced options --- scripts/automation/trex_control_plane/console/line_parsing.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 scripts/automation/trex_control_plane/console/line_parsing.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/console/line_parsing.py b/scripts/automation/trex_control_plane/console/line_parsing.py new file mode 100644 index 00000000..f97bce4c --- /dev/null +++ b/scripts/automation/trex_control_plane/console/line_parsing.py @@ -0,0 +1 @@ +__author__ = 'danklei' -- cgit From 0ceddc74c938a023c515be4ed2c37198fd66e87e Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 3 Nov 2015 09:37:42 +0200 Subject: first commit for advnaced options --- scripts/automation/trex_control_plane/client/trex_async_client.py | 2 +- .../automation/trex_control_plane/client/trex_stateless_client.py | 8 ++++++-- .../automation/trex_control_plane/client_utils/general_utils.py | 2 +- .../automation/trex_control_plane/client_utils/jsonrpc_client.py | 3 +++ scripts/automation/trex_control_plane/console/line_parsing.py | 4 ++++ scripts/automation/trex_control_plane/console/trex_console.py | 7 +++---- 6 files changed, 18 insertions(+), 8 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index 1ce10288..49ef9506 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -131,7 +131,7 @@ class TrexAsyncStatsManager(): -class TrexAsyncClient(): +class CTRexAsyncClient(): def __init__ (self, port): self.port = port diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index c180e0d1..6b1b7b94 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -13,7 +13,7 @@ from common.trex_stats import * from common.trex_streams import * from collections import namedtuple -from trex_async_client import TrexAsyncClient +from trex_async_client import CTRexAsyncClient RpcCmdData = namedtuple('RpcCmdData', ['method', 'params']) @@ -41,7 +41,7 @@ class CTRexStatelessClient(object): self._server_version = None self.__err_log = None - self._async_client = TrexAsyncClient(async_port) + self._async_client = CTRexAsyncClient(async_port) # ----- decorator methods ----- # @@ -136,6 +136,10 @@ class CTRexStatelessClient(object): else: return port_ids + def sync_user(self): + return self.transmit("sync_user") + + def get_acquired_ports(self): return self._conn_handler.keys() diff --git a/scripts/automation/trex_control_plane/client_utils/general_utils.py b/scripts/automation/trex_control_plane/client_utils/general_utils.py index 3c025608..69ad14b2 100755 --- a/scripts/automation/trex_control_plane/client_utils/general_utils.py +++ b/scripts/automation/trex_control_plane/client_utils/general_utils.py @@ -24,7 +24,7 @@ def user_input(): def get_current_user(): if pwd: - return pwd.getpwuid( os.geteuid() ).pw_name + return pwd.getpwuid(os.geteuid()).pw_name else: return getpass.getuser() diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index fe94e5ef..58491aba 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -514,3 +514,6 @@ class TrexStatelessClient(JsonRpcClient): return True, resp_list # return self.invoke_rpc_method('add_stream', params = params) + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/console/line_parsing.py b/scripts/automation/trex_control_plane/console/line_parsing.py index f97bce4c..34776424 100644 --- a/scripts/automation/trex_control_plane/console/line_parsing.py +++ b/scripts/automation/trex_control_plane/console/line_parsing.py @@ -1 +1,5 @@ __author__ = 'danklei' + + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 549262c5..e707a9e1 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -31,8 +31,7 @@ import trex_root_path from common.trex_streams import * from client.trex_stateless_client import CTRexStatelessClient from common.text_opts import * -from client_utils.general_utils import user_input - +from client_utils.general_utils import user_input, get_current_user import trex_status from collections import namedtuple @@ -778,8 +777,8 @@ def setParserOptions(): default = 4500, type = int) - parser.add_argument("-u", "--user", help = "User Name [default is random generated]\n", - default = 'user_' + ''.join(random.choice(string.digits) for _ in range(5)), + parser.add_argument("-u", "--user", help = "User Name [default is currently logged in user]\n", + default = get_current_user(), type = str) parser.add_argument("--verbose", dest="verbose", -- cgit From c2928039f199d3cef6d130201cf825d5b6b67937 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 3 Nov 2015 10:10:52 +0200 Subject: fields for specific ports on the status screen --- .../trex_control_plane/client/trex_async_client.py | 40 ++++++++++++++------ .../trex_control_plane/console/trex_status.py | 44 +++++++++------------- 2 files changed, 47 insertions(+), 37 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index 1ce10288..4c17603d 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -25,6 +25,15 @@ class TrexAsyncStats(object): self.current = {} self.last_update_ts = datetime.datetime.now() + def __format_num (self, size, suffix = ""): + + for unit in ['','K','M','G','T','P']: + if abs(size) < 1000.0: + return "%3.2f %s%s" % (size, unit, suffix) + size /= 1000.0 + + return "NaN" + def update (self, snapshot): #update @@ -36,18 +45,25 @@ class TrexAsyncStats(object): self.ref_point = self.current - def get (self, field): + def get (self, field, format = False, suffix = ""): if not field in self.current: - return 0 + return "N/A" - return self.current[field] + if not format: + return self.current[field] + else: + return self.__format_num(self.current[field], suffix) - def get_rel (self, field): + + def get_rel (self, field, format = False, suffix = ""): if not field in self.current: - return 0 + return "N/A" - return self.current[field] - self.ref_point[field] + if not format: + return (self.current[field] - self.ref_point[field]) + else: + return self.__format_num(self.current[field] - self.ref_point[field], suffix) # return true if new data has arrived in the past 2 seconds @@ -80,10 +96,10 @@ class TrexAsyncStatsManager(): def get_port_stats (self, port_id): - if not port_id in self.port_stats: + if not str(port_id) in self.port_stats: return None - return self.port_stats[port_id] + return self.port_stats[str(port_id)] def update (self, snapshot): @@ -103,14 +119,16 @@ class TrexAsyncStatsManager(): for key, value in snapshot.iteritems(): # match a pattern of ports - m = re.search('.*\-([0-8])', key) + m = re.search('(.*)\-([0-8])', key) if m: - port_id = m.group(1) + + port_id = m.group(2) + field_name = m.group(1) if not port_id in port_stats: port_stats[port_id] = {} - port_stats[port_id][key] = value + port_stats[port_id][field_name] = value else: # no port match - general stats diff --git a/scripts/automation/trex_control_plane/console/trex_status.py b/scripts/automation/trex_control_plane/console/trex_status.py index 4e73e0bb..ac8e7411 100644 --- a/scripts/automation/trex_control_plane/console/trex_status.py +++ b/scripts/automation/trex_control_plane/console/trex_status.py @@ -18,15 +18,6 @@ def percentage (a, total): x = int ((float(a) / total) * 100) return str(x) + "%" -# simple float to human readable -def float_to_human_readable (size, suffix = "bps"): - for unit in ['','K','M','G','T']: - if abs(size) < 1000.0: - return "%3.2f %s%s" % (size, unit, suffix) - size /= 1000.0 - return "NaN" - - ################### panels ################# # panel object @@ -37,6 +28,8 @@ class TrexStatusPanel(object): self.log = status_obj.log self.stateless_client = status_obj.stateless_client + + self.stats = status_obj.stats self.general_stats = status_obj.general_stats self.h = h @@ -120,17 +113,17 @@ class GeneralInfoPanel(TrexStatusPanel): self.getwin().addstr(3, 2, "{:<30} {:0.2f} %".format("CPU util.:", self.general_stats.get("m_cpu_util"))) self.getwin().addstr(5, 2, "{:<30} {:} / {:}".format("Total Tx. rate:", - float_to_human_readable(self.general_stats.get("m_tx_bps")), - float_to_human_readable(self.general_stats.get("m_tx_pps"), suffix = "pps"))) + self.general_stats.get("m_tx_bps", format = True, suffix = "bps"), + self.general_stats.get("m_tx_pps", format = True, suffix = "pps"))) # missing RX field #self.getwin().addstr(5, 2, "{:<30} {:} / {:}".format("Total Rx. rate:", - # float_to_human_readable(self.general_stats.get("m_rx_bps")), - # float_to_human_readable(self.general_stats.get("m_rx_pps"), suffix = "pps"))) + # self.general_stats.get("m_rx_bps"), + # self.general_stats.get("m_rx_pps"), suffix = "pps")) self.getwin().addstr(7, 2, "{:<30} {:} / {:}".format("Total Tx:", - float_to_human_readable(self.general_stats.get_rel("m_total_tx_bytes"), suffix = "B"), - float_to_human_readable(self.general_stats.get_rel("m_total_tx_pkts"), suffix = "pkts"))) + self.general_stats.get_rel("m_total_tx_bytes", format = True, suffix = "B"), + self.general_stats.get_rel("m_total_tx_pkts", format = True, suffix = "pkts"))) # all ports stats class PortsStatsPanel(TrexStatusPanel): @@ -142,7 +135,6 @@ class PortsStatsPanel(TrexStatusPanel): def draw (self): self.clear() - return owned_ports = self.status_obj.owned_ports if not owned_ports: @@ -153,23 +145,22 @@ class PortsStatsPanel(TrexStatusPanel): self.getwin().addstr(3, 2, "{:^15} {:^15} {:^15} {:^15} {:^15} {:^15} {:^15}".format( "Port ID", "Tx [pps]", "Tx [bps]", "Tx [bytes]", "Rx [pps]", "Rx [bps]", "Rx [bytes]")) - # port loop - self.status_obj.stats.query_sync() + for i, port_index in enumerate(owned_ports): port_stats = self.status_obj.stats.get_port_stats(port_index) if port_stats: - self.getwin().addstr(5 + (i * 4), 2, "{:^15} {:^15,.2f} {:^15,.2f} {:^15,} {:^15,.2f} {:^15,.2f} {:^15,}".format( + self.getwin().addstr(5 + (i * 4), 2, "{:^15} {:^15} {:^15} {:^15} {:^15} {:^15} {:^15}".format( "{0} ({1})".format(str(port_index), self.status_obj.server_sys_info["ports"][port_index]["speed"]), - port_stats["tx_pps"], - port_stats["tx_bps"], - port_stats["total_tx_bytes"], - port_stats["rx_pps"], - port_stats["rx_bps"], - port_stats["total_rx_bytes"])) - + port_stats.get("m_total_tx_pps", format = True, suffix = "pps"), + port_stats.get("m_total_tx_bps", format = True, suffix = "bps"), + port_stats.get_rel("obytes", format = True, suffix = "B"), + port_stats.get("m_total_rx_pps", format = True, suffix = "pps"), + port_stats.get("m_total_rx_bps", format = True, suffix = "bps"), + port_stats.get_rel("ibytes", format = True, suffix = "B"))) + else: self.getwin().addstr(5 + (i * 4), 2, "{:^15} {:^15} {:^15} {:^15} {:^15} {:^15} {:^15}".format( "{0} ({1})".format(str(port_index), self.status_obj.server_sys_info["ports"][port_index]["speed"]), @@ -374,6 +365,7 @@ class TrexStatus(): self.log = TrexStatusLog() self.cmds = TrexStatusCommands(self) + self.stats = stateless_client.get_stats_async() self.general_stats = stateless_client.get_stats_async().get_general_stats() # fetch server info -- cgit From 597f74d8ed10abc3dd9df7e81ecea5ac2f5c714e Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 3 Nov 2015 11:27:48 +0200 Subject: updated sync_client --- .../trex_control_plane/client/trex_stateless_client.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 6b1b7b94..aeb25422 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -136,9 +136,8 @@ class CTRexStatelessClient(object): else: return port_ids - def sync_user(self): - return self.transmit("sync_user") - + def sync_user(self, sync_streams=False): + return self.transmit("sync_user", {"user": self.user, "sync_streams": sync_streams}) def get_acquired_ports(self): return self._conn_handler.keys() @@ -363,8 +362,21 @@ class CTRexStatelessClient(object): if self.server_version == "Unknown" or self.system_info == "Unknown": self.disconnect() return False, self.__err_log + # sync with previous session + res_ok, port_info = self.sync_user() + if not res_ok: + self.disconnect() + return False, port_info + else: + # handle sync data + for port in port_info: + self._conn_handler[port.get("port_id")] = port.get("handler") + if port.get("state") == "transmitting": + # port is active + self._active_ports.add(port.get("port_id")) return True, "" + def transmit(self, method_name, params={}): return self.comm_link.transmit(method_name, params) -- cgit From 30b87959dc2d8da7f2f2a471a53485d600d8735a Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 4 Nov 2015 11:31:30 +0200 Subject: some fields were missing from the ZMQ publisher --- .../trex_control_plane/console/trex_console.py | 4 +++- .../trex_control_plane/console/trex_status.py | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index e707a9e1..8a5b29cc 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -257,7 +257,9 @@ class TRexConsole(cmd.Cmd): # make sure that the user wants to acquire all args = line.split() if len(args) < 1: - print "Please provide a list of ports separated by spaces, or specify 'all' to acquire all available ports" + print magenta("Please provide a list of ports separated by spaces, or specify 'all' to acquire all available ports") + return + if args[0] == "all": ask = ConfirmMenu('Are you sure you want to acquire all ports ? ') rc = ask.show() diff --git a/scripts/automation/trex_control_plane/console/trex_status.py b/scripts/automation/trex_control_plane/console/trex_status.py index ac8e7411..a19587a3 100644 --- a/scripts/automation/trex_control_plane/console/trex_status.py +++ b/scripts/automation/trex_control_plane/console/trex_status.py @@ -112,19 +112,24 @@ class GeneralInfoPanel(TrexStatusPanel): self.getwin().addstr(3, 2, "{:<30} {:0.2f} %".format("CPU util.:", self.general_stats.get("m_cpu_util"))) - self.getwin().addstr(5, 2, "{:<30} {:} / {:}".format("Total Tx. rate:", + self.getwin().addstr(6, 2, "{:<30} {:} / {:}".format("Total Tx. rate:", self.general_stats.get("m_tx_bps", format = True, suffix = "bps"), self.general_stats.get("m_tx_pps", format = True, suffix = "pps"))) - # missing RX field - #self.getwin().addstr(5, 2, "{:<30} {:} / {:}".format("Total Rx. rate:", - # self.general_stats.get("m_rx_bps"), - # self.general_stats.get("m_rx_pps"), suffix = "pps")) - - self.getwin().addstr(7, 2, "{:<30} {:} / {:}".format("Total Tx:", + + self.getwin().addstr(8, 2, "{:<30} {:} / {:}".format("Total Tx:", self.general_stats.get_rel("m_total_tx_bytes", format = True, suffix = "B"), self.general_stats.get_rel("m_total_tx_pkts", format = True, suffix = "pkts"))) + self.getwin().addstr(11, 2, "{:<30} {:} / {:}".format("Total Rx. rate:", + self.general_stats.get("m_rx_bps", format = True, suffix = "bps"), + self.general_stats.get("m_rx_pps", format = True, suffix = "pps"))) + + + self.getwin().addstr(13, 2, "{:<30} {:} / {:}".format("Total Rx:", + self.general_stats.get_rel("m_total_rx_bytes", format = True, suffix = "B"), + self.general_stats.get_rel("m_total_rx_pkts", format = True, suffix = "pkts"))) + # all ports stats class PortsStatsPanel(TrexStatusPanel): def __init__ (self, h, l, y, x, status_obj): -- cgit From 530625493e92ac300b8a1135d061a3ecd428008d Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 4 Nov 2015 13:35:08 +0200 Subject: new format for the status per port --- .../trex_control_plane/console/trex_status.py | 45 ++++++++++++++++------ 1 file changed, 33 insertions(+), 12 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/console/trex_status.py b/scripts/automation/trex_control_plane/console/trex_status.py index a19587a3..c410e7c5 100644 --- a/scripts/automation/trex_control_plane/console/trex_status.py +++ b/scripts/automation/trex_control_plane/console/trex_status.py @@ -147,8 +147,8 @@ class PortsStatsPanel(TrexStatusPanel): return # table header - self.getwin().addstr(3, 2, "{:^15} {:^15} {:^15} {:^15} {:^15} {:^15} {:^15}".format( - "Port ID", "Tx [pps]", "Tx [bps]", "Tx [bytes]", "Rx [pps]", "Rx [bps]", "Rx [bytes]")) + self.getwin().addstr(3, 2, "{:^15} {:^30} {:^30} {:^30}".format( + "Port ID", "Tx Rate [bps/pps]", "Rx Rate [bps/pps]", "Total Bytes [tx/rx]")) @@ -157,25 +157,46 @@ class PortsStatsPanel(TrexStatusPanel): port_stats = self.status_obj.stats.get_port_stats(port_index) if port_stats: - self.getwin().addstr(5 + (i * 4), 2, "{:^15} {:^15} {:^15} {:^15} {:^15} {:^15} {:^15}".format( + self.getwin().addstr(5 + (i * 4), 2, "{:^15} {:^30} {:^30} {:^30}".format( "{0} ({1})".format(str(port_index), self.status_obj.server_sys_info["ports"][port_index]["speed"]), - port_stats.get("m_total_tx_pps", format = True, suffix = "pps"), - port_stats.get("m_total_tx_bps", format = True, suffix = "bps"), - port_stats.get_rel("obytes", format = True, suffix = "B"), - port_stats.get("m_total_rx_pps", format = True, suffix = "pps"), - port_stats.get("m_total_rx_bps", format = True, suffix = "bps"), - port_stats.get_rel("ibytes", format = True, suffix = "B"))) + "{0} / {1}".format(port_stats.get("m_total_tx_bps", format = True, suffix = "bps"), + port_stats.get("m_total_tx_pps", format = True, suffix = "pps")), + + "{0} / {1}".format(port_stats.get("m_total_rx_bps", format = True, suffix = "bps"), + port_stats.get("m_total_rx_pps", format = True, suffix = "pps")), + "{0} / {1}".format(port_stats.get_rel("obytes", format = True, suffix = "B"), + port_stats.get_rel("ibytes", format = True, suffix = "B")))) else: - self.getwin().addstr(5 + (i * 4), 2, "{:^15} {:^15} {:^15} {:^15} {:^15} {:^15} {:^15}".format( + self.getwin().addstr(5 + (i * 4), 2, 2, "{:^15} {:^30} {:^30} {:^30}".format( "{0} ({1})".format(str(port_index), self.status_obj.server_sys_info["ports"][port_index]["speed"]), "N/A", "N/A", "N/A", - "N/A", - "N/A", "N/A")) + + # old format +# if port_stats: +# self.getwin().addstr(5 + (i * 4), 2, "{:^15} {:^15} {:^15} {:^15} {:^15} {:^15} {:^15}".format( +# "{0} ({1})".format(str(port_index), self.status_obj.server_sys_info["ports"][port_index]["speed"]), +# port_stats.get("m_total_tx_pps", format = True, suffix = "pps"), +# port_stats.get("m_total_tx_bps", format = True, suffix = "bps"), +# port_stats.get_rel("obytes", format = True, suffix = "B"), +# port_stats.get("m_total_rx_pps", format = True, suffix = "pps"), +# port_stats.get("m_total_rx_bps", format = True, suffix = "bps"), +# port_stats.get_rel("ibytes", format = True, suffix = "B"))) +# +# else: +# self.getwin().addstr(5 + (i * 4), 2, "{:^15} {:^15} {:^15} {:^15} {:^15} {:^15} {:^15}".format( +# "{0} ({1})".format(str(port_index), self.status_obj.server_sys_info["ports"][port_index]["speed"]), +# "N/A", +# "N/A", +# "N/A", +# "N/A", +# "N/A", +# "N/A")) + # control panel class ControlPanel(TrexStatusPanel): def __init__ (self, h, l, y, x, status_obj): -- cgit From 36a9677c0abc038235e7bf706cb2b3dc9e720284 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 5 Nov 2015 07:35:15 +0200 Subject: added custom line parsing class and methods --- .../trex_control_plane/console/line_parsing.py | 47 +++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/console/line_parsing.py b/scripts/automation/trex_control_plane/console/line_parsing.py index 34776424..c1227a39 100644 --- a/scripts/automation/trex_control_plane/console/line_parsing.py +++ b/scripts/automation/trex_control_plane/console/line_parsing.py @@ -1,5 +1,50 @@ -__author__ = 'danklei' +import argparse +from collections import namedtuple +import sys +ArgumentPack = namedtuple('ArgumentPack', ['name_or_flags', 'options']) +# class ArgumentPack(namedtuple('ArgumentPack', ['name_or_flags', 'options'])): +# +# @property +# def name_or_flags(self): +# return self.name_or_flags +# +# @name_or_flags.setter +# def name_or_flags(self, val): +# print "bla" +# if not isinstance(val, list): +# self.name_or_flags = [val] +# else: +# self.name_or_flags = val + + +OPTIONS_DB = {'-m': ArgumentPack(['-m', '--multiplier'], + {'help': "Set multiplier for stream", 'dest':"mult"}), + 'file_path': ArgumentPack(['file'], + {'help': "File path to yaml file"})} + + +class CCmdArgParser(argparse.ArgumentParser): + + def __init__(self, *args, **kwargs): + super(CCmdArgParser, self).__init__(*args, **kwargs) + pass + + def error(self, message): + # self.print_usage(sys.stderr) + self.print_help() + return + +def gen_parser(op_name, *args): + parser = CCmdArgParser(prog=op_name, conflict_handler='resolve') + for param in args: + try: + parser.add_argument(*OPTIONS_DB[param].name_or_flags, + **OPTIONS_DB[param].options) + except KeyError, e: + cause = e.args[0] + raise KeyError("The attribute '{0}' is missing as a field of the {1} option.\n".format(cause, param)) + return parser if __name__ == "__main__": pass \ No newline at end of file -- cgit From e92507617ed8069b674fa5729b1e6a0c5d4b2662 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 5 Nov 2015 09:55:03 +0200 Subject: bug fixes in status window --- .../trex_control_plane/client/trex_async_client.py | 3 +- .../client/trex_stateless_client.py | 17 ++- .../trex_control_plane/console/trex_console.py | 1 + .../trex_control_plane/console/trex_status.py | 122 ++++++++++++++------- 4 files changed, 98 insertions(+), 45 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index 419448bb..72cce5aa 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -82,6 +82,8 @@ class TrexAsyncStatsPort(TrexAsyncStats): def __init__ (self): super(TrexAsyncStatsPort, self).__init__() + def get_stream_stats (self, stream_id): + return None # stats manager class TrexAsyncStatsManager(): @@ -101,7 +103,6 @@ class TrexAsyncStatsManager(): return self.port_stats[str(port_id)] - def update (self, snapshot): if snapshot['name'] == 'trex-global': diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index aeb25422..db51683a 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -254,7 +254,17 @@ class CTRexStatelessClient(object): self.ack_success_test) pass - @force_status(owned=True, active_and_owned=True) + + @force_status(owned=True)#, active_and_owned=True) + def get_all_streams(self, port_id, get_pkt = False): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id, + "get_pkt": get_pkt} + return self.transmit("get_all_streams", params) + + @force_status(owned=True)#, active_and_owned=True) def get_stream_id_list(self, port_id=None): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") @@ -263,12 +273,13 @@ class CTRexStatelessClient(object): return self.transmit("get_stream_list", params) @force_status(owned=True, active_and_owned=True) - def get_stream(self, stream_id, port_id=None): + def get_stream(self, stream_id, port_id, get_pkt = False): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") params = {"handler": self._conn_handler.get(port_id), "port_id": port_id, - "stream_id": stream_id} + "stream_id": stream_id, + "get_pkt": get_pkt} return self.transmit("get_stream_list", params) @force_status(owned=True) diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 8a5b29cc..52effa7e 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -251,6 +251,7 @@ class TRexConsole(cmd.Cmd): return port_list + def do_acquire(self, line, force=False): '''Acquire ports\n''' diff --git a/scripts/automation/trex_control_plane/console/trex_status.py b/scripts/automation/trex_control_plane/console/trex_status.py index c410e7c5..2b97d7d3 100644 --- a/scripts/automation/trex_control_plane/console/trex_status.py +++ b/scripts/automation/trex_control_plane/console/trex_status.py @@ -90,7 +90,7 @@ class ServerInfoPanel(TrexStatusPanel): self.getwin().addstr(9, 2, "{:<30} {:<30}".format("Ports Count:", self.status_obj.server_sys_info["port_count"])) - ports_owned = " ".join(str(x) for x in self.status_obj.owned_ports) + ports_owned = " ".join(str(x) for x in self.status_obj.owned_ports_list) if not ports_owned: ports_owned = "None" @@ -141,7 +141,7 @@ class PortsStatsPanel(TrexStatusPanel): self.clear() - owned_ports = self.status_obj.owned_ports + owned_ports = self.status_obj.owned_ports_list if not owned_ports: self.getwin().addstr(3, 2, "No Owned Ports - Please Acquire One Or More Ports") return @@ -225,7 +225,7 @@ class SinglePortPanel(TrexStatusPanel): self.clear() - if not self.port_id in self.status_obj.stateless_client.get_owned_ports(): + if not self.port_id in self.status_obj.owned_ports_list: self.getwin().addstr(y, 2, "Port {0} is not owned by you, please acquire the port for more info".format(self.port_id)) return @@ -239,16 +239,19 @@ class SinglePortPanel(TrexStatusPanel): y += 2 # streams - if 'streams' in self.status_obj.snapshot[self.port_id]: - for stream_id, stream in self.status_obj.snapshot[self.port_id]['streams'].iteritems(): + + if 'streams' in self.status_obj.owned_ports[str(self.port_id)]: + stream_info = self.status_obj.owned_ports[str(self.port_id)]['streams'] + + for stream_id, stream in sorted(stream_info.iteritems(), key=operator.itemgetter(0)): self.getwin().addstr(y, 2, "{:^15} {:^15} {:^15} {:^15} {:^15} {:^15} {:^15}".format( stream_id, - ("True" if stream['stream']['enabled'] else "False"), - stream['stream']['mode']['type'], - ("True" if stream['stream']['self_start'] else "False"), - stream['stream']['isg'], - (stream['stream']['next_stream_id'] if stream['stream']['next_stream_id'] != -1 else "None"), - ("{0} instr.".format(len(stream['stream']['vm'])) if stream['stream']['vm'] else "None"))) + ("True" if stream['enabled'] else "False"), + stream['mode']['type'], + ("True" if stream['self_start'] else "False"), + stream['isg'], + (stream['next_stream_id'] if stream['next_stream_id'] != -1 else "None"), + ("{0} instr.".format(len(stream['vm'])) if stream['vm'] else "None"))) y += 1 @@ -258,37 +261,36 @@ class SinglePortPanel(TrexStatusPanel): self.getwin().addstr(y, 2, "Traffic:", curses.A_UNDERLINE) y += 2 - self.status_obj.stats.query_sync() - port_stats = self.status_obj.stats.get_port_stats(self.port_id) - # table header - self.getwin().addstr(y, 2, "{:^15} {:^15} {:^15} {:^15} {:^15} {:^15} {:^15}".format( - "Port ID", "Tx [pps]", "Tx [bps]", "Tx [bytes]", "Rx [pps]", "Rx [bps]", "Rx [bytes]")) + # table header + self.getwin().addstr(y, 2, "{:^15} {:^30} {:^30} {:^30}".format( + "Port ID", "Tx Rate [bps/pps]", "Rx Rate [bps/pps]", "Total Bytes [tx/rx]")) + y += 2 - if port_stats: - self.getwin().addstr(y, 2, "{:^15} {:^15,} {:^15,} {:^15,} {:^15,} {:^15,} {:^15,}".format( - "{0} ({1})".format(str(self.port_id), self.status_obj.server_sys_info["ports"][self.port_id]["speed"]), - port_stats["tx_pps"], - port_stats["tx_bps"], - port_stats["total_tx_bytes"], - port_stats["rx_pps"], - port_stats["rx_bps"], - port_stats["total_rx_bytes"])) + port_stats = self.status_obj.stats.get_port_stats(self.port_id) + if port_stats: + self.getwin().addstr(y, 2, "{:^15} {:^30} {:^30} {:^30}".format( + "{0} ({1})".format(str(self.port_id), self.status_obj.server_sys_info["ports"][self.port_id]["speed"]), + "{0} / {1}".format(port_stats.get("m_total_tx_bps", format = True, suffix = "bps"), + port_stats.get("m_total_tx_pps", format = True, suffix = "pps")), + + "{0} / {1}".format(port_stats.get("m_total_rx_bps", format = True, suffix = "bps"), + port_stats.get("m_total_rx_pps", format = True, suffix = "pps")), + "{0} / {1}".format(port_stats.get_rel("obytes", format = True, suffix = "B"), + port_stats.get_rel("ibytes", format = True, suffix = "B")))) + else: - self.getwin().addstr(y, 2, "{:^15} {:^15} {:^15} {:^15} {:^15} {:^15} {:^15}".format( + self.getwin().addstr(y + (i * 4), 2, 2, "{:^15} {:^30} {:^30} {:^30}".format( "{0} ({1})".format(str(self.port_id), self.status_obj.server_sys_info["ports"][self.port_id]["speed"]), "N/A", "N/A", "N/A", - "N/A", - "N/A", "N/A")) - y += 2 ################### main objects ################# @@ -388,7 +390,7 @@ class TrexStatus(): self.stateless_client = stateless_client - self.log = TrexStatusLog() + self.log = TrexStatusLog() self.cmds = TrexStatusCommands(self) self.stats = stateless_client.get_stats_async() @@ -403,9 +405,35 @@ class TrexStatus(): if not rc: return - self.owned_ports = self.stateless_client.get_acquired_ports() + # list of owned ports + self.owned_ports_list = self.stateless_client.get_acquired_ports() + + # data per port + self.owned_ports = {} + + for port_id in self.owned_ports_list: + self.owned_ports[str(port_id)] = {} + self.owned_ports[str(port_id)]['streams'] = {} + + rc, stream_list = self.stateless_client.get_all_streams(port_id) + if not rc: + raise Exception("unable to get streams") + self.owned_ports[str(port_id)] = stream_list + + try: + curses.curs_set(0) + except: + pass + + curses.use_default_colors() + self.stdscr.nodelay(1) + curses.nonl() + curses.noecho() + + self.generate_layout() + def generate_layout (self): self.max_y = self.stdscr.getmaxyx()[0] @@ -441,17 +469,22 @@ class TrexStatus(): # main run entry point def run (self): - try: - curses.curs_set(0) - except: - pass - curses.use_default_colors() - self.stdscr.nodelay(1) - curses.nonl() - curses.noecho() + # list of owned ports + self.owned_ports_list = self.stateless_client.get_acquired_ports() - self.generate_layout() + # data per port + self.owned_ports = {} + + for port_id in self.owned_ports_list: + self.owned_ports[str(port_id)] = {} + self.owned_ports[str(port_id)]['streams'] = {} + + rc, stream_list = self.stateless_client.get_all_streams(port_id) + if not rc: + raise Exception("unable to get streams") + + self.owned_ports[str(port_id)] = stream_list self.update_active = True while (True): @@ -473,8 +506,15 @@ class TrexStatus(): sleep(0.01) +# global container +trex_status = None + def show_trex_status_internal (stdscr, stateless_client): - trex_status = TrexStatus(stdscr, stateless_client) + global trex_status + + if trex_status == None: + trex_status = TrexStatus(stdscr, stateless_client) + trex_status.run() def show_trex_status (stateless_client): -- cgit From 953a250e6cbaea3040920e7441d2d019705efe51 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sat, 7 Nov 2015 13:37:31 +0200 Subject: Extended line parsing options, didn't apply all changes on console YET --- .../client/trex_stateless_client.py | 51 +++++++++- .../trex_control_plane/console/line_parsing.py | 50 ---------- .../trex_control_plane/console/parsing_opts.py | 103 +++++++++++++++++++ .../trex_control_plane/console/trex_console.py | 111 +++++++++++++-------- 4 files changed, 222 insertions(+), 93 deletions(-) delete mode 100644 scripts/automation/trex_control_plane/console/line_parsing.py create mode 100755 scripts/automation/trex_control_plane/console/parsing_opts.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index aeb25422..dc6e6be2 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -45,16 +45,57 @@ class CTRexStatelessClient(object): # ----- decorator methods ----- # + def acquired(func): + def wrapper_f(self, *args, **kwargs): + # print func.__name__ + # print args + # print kwargs + port_ids = kwargs.get("port_id") + if not port_ids: + # print "FROM ARGS!" + # print args + port_ids = args[0] + if isinstance(port_ids, int): + # make sure port_ids is a list + port_ids = [port_ids] + bad_ids = set() + # print "=============" + # print port_ids + for port_id in port_ids: + port_owned = self._conn_handler.get(port_id) + if not port_owned: + bad_ids.add(port_id) + # elif active_and_owned: # stronger condition than just owned, hence gets precedence + # if port_owned and port_id in self._active_ports: + # continue + # else: + # bad_ids.add(port_id) + else: + continue + if bad_ids: + # Some port IDs are not according to desires status + raise ValueError("The requested method ('{0}') cannot be invoked since port IDs {1} are not " + "at allowed states".format(func.__name__, list(bad_ids))) + else: + return func(self, *args, **kwargs) + return wrapper_f + def force_status(owned=True, active_and_owned=False): def wrapper(func): def wrapper_f(self, *args, **kwargs): + # print args + # print kwargs port_ids = kwargs.get("port_id") if not port_ids: + print "FROM ARGS!" + print args port_ids = args[0] if isinstance(port_ids, int): # make sure port_ids is a list port_ids = [port_ids] bad_ids = set() + # print "=============" + # print port_ids for port_id in port_ids: port_owned = self._conn_handler.get(port_id) if owned and not port_owned: @@ -271,14 +312,16 @@ class CTRexStatelessClient(object): "stream_id": stream_id} return self.transmit("get_stream_list", params) - @force_status(owned=True) - def start_traffic(self, port_id=None): + @acquired + def start_traffic(self, multiplier, port_id=None): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications - commands = [RpcCmdData("start_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id, "mul": 1.0}) + commands = [RpcCmdData("start_traffic", {"handler": self._conn_handler.get(p_id), + "port_id": p_id, + "mul": multiplier}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) if rc: @@ -287,7 +330,7 @@ class CTRexStatelessClient(object): else: params = {"handler": self._conn_handler.get(port_id), "port_id": port_id, - "mul": 1.0} + "mul": multiplier} command = RpcCmdData("start_traffic", params) return self._handle_start_traffic_response(command, self.transmit(command.method, command.params), diff --git a/scripts/automation/trex_control_plane/console/line_parsing.py b/scripts/automation/trex_control_plane/console/line_parsing.py deleted file mode 100644 index c1227a39..00000000 --- a/scripts/automation/trex_control_plane/console/line_parsing.py +++ /dev/null @@ -1,50 +0,0 @@ -import argparse -from collections import namedtuple -import sys - -ArgumentPack = namedtuple('ArgumentPack', ['name_or_flags', 'options']) -# class ArgumentPack(namedtuple('ArgumentPack', ['name_or_flags', 'options'])): -# -# @property -# def name_or_flags(self): -# return self.name_or_flags -# -# @name_or_flags.setter -# def name_or_flags(self, val): -# print "bla" -# if not isinstance(val, list): -# self.name_or_flags = [val] -# else: -# self.name_or_flags = val - - -OPTIONS_DB = {'-m': ArgumentPack(['-m', '--multiplier'], - {'help': "Set multiplier for stream", 'dest':"mult"}), - 'file_path': ArgumentPack(['file'], - {'help': "File path to yaml file"})} - - -class CCmdArgParser(argparse.ArgumentParser): - - def __init__(self, *args, **kwargs): - super(CCmdArgParser, self).__init__(*args, **kwargs) - pass - - def error(self, message): - # self.print_usage(sys.stderr) - self.print_help() - return - -def gen_parser(op_name, *args): - parser = CCmdArgParser(prog=op_name, conflict_handler='resolve') - for param in args: - try: - parser.add_argument(*OPTIONS_DB[param].name_or_flags, - **OPTIONS_DB[param].options) - except KeyError, e: - cause = e.args[0] - raise KeyError("The attribute '{0}' is missing as a field of the {1} option.\n".format(cause, param)) - return parser - -if __name__ == "__main__": - pass \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py new file mode 100755 index 00000000..c94a7461 --- /dev/null +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -0,0 +1,103 @@ +import argparse +from collections import namedtuple +import sys + +ArgumentPack = namedtuple('ArgumentPack', ['name_or_flags', 'options']) +ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options']) + + +# list of available parsing options +MULTIPLIER = 1 +PORT_LIST = 2 +ALL_PORTS = 3 +PORT_LIST_WITH_ALL = 4 +FILE_PATH = 5 +FILE_FROM_DB = 6 +STREAM_FROM_PATH_OR_FILE = 7 + +# list of ArgumentGroup types +MUTEX = 1 + + + + +OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], + {'help': "Set multiplier for stream", 'dest': "mult", 'type': float}), + PORT_LIST: ArgumentPack(['--port'], + {"nargs": '+', + # "action": "store_" + 'help': "A list of ports on which to apply the command", + 'default': []}), + ALL_PORTS: ArgumentPack(['-a'], + {"action": "store_true", + "dest": "all", + 'help': "Set this flag to apply the command on all available ports"}), + + FILE_PATH: ArgumentPack(['-f'], + {'help': "File path to YAML file that describes a stream pack"}), + FILE_FROM_DB: ArgumentPack(['--db'], + {'help': "A stream pack which already loaded into console cache."}), + # advanced options + PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST, + ALL_PORTS], + {'required': True}), + STREAM_FROM_PATH_OR_FILE: ArgumentGroup(MUTEX, [FILE_PATH, + FILE_FROM_DB], + {'required': True}) + } + + +class CCmdArgParser(argparse.ArgumentParser): + + def __init__(self, *args, **kwargs): + super(CCmdArgParser, self).__init__(*args, **kwargs) + pass + + # def error(self, message): + # try: + # super(CCmdArgParser, self).error(message) # this will trigger system exit! + # except SystemExit: + # return -1 + # + # # self.print_usage(sys.stderr) + # # print ('%s: error: %s\n') % (self.prog, message) + # # self.print_help() + # return + + def exit(self, status=0, message=None): + try: + super(CCmdArgParser, self).exit(status, message) # this will trigger system exit! + except SystemExit: + return -1 + return + +def gen_parser(op_name, description, *args): + parser = CCmdArgParser(prog=op_name, conflict_handler='resolve', + # add_help=False, + description=description) + for param in args: + try: + argument = OPTIONS_DB[param] + if isinstance(argument, ArgumentGroup): + if argument.type == MUTEX: + # handle as mutually exclusive group + group = parser.add_mutually_exclusive_group(**argument.options) + for sub_argument in argument.args: + group.add_argument(*OPTIONS_DB[sub_argument].name_or_flags, + **OPTIONS_DB[sub_argument].options) + else: + # ignore invalid objects + continue + elif isinstance(argument, ArgumentPack): + parser.add_argument(*argument.name_or_flags, + **argument.options) + else: + # ignore invalid objects + continue + except KeyError as e: + cause = e.args[0] + raise KeyError("The attribute '{0}' is missing as a field of the {1} option.\n".format(cause, param)) + return parser + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 8a5b29cc..33b4e164 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -32,7 +32,7 @@ from common.trex_streams import * from client.trex_stateless_client import CTRexStatelessClient from common.text_opts import * from client_utils.general_utils import user_input, get_current_user - +import parsing_opts import trex_status from collections import namedtuple @@ -234,6 +234,9 @@ class TRexConsole(cmd.Cmd): def extract_port_ids_from_line(self, line): return {int(x) for x in line.split()} + def extract_port_ids_from_list(self, port_list): + return {int(x) for x in port_list} + def parse_ports_from_line (self, line): port_list = set() if line: @@ -449,42 +452,42 @@ class TRexConsole(cmd.Cmd): def default(self, line): print "'{0}' is an unrecognized command. type 'help' or '?' for a list\n".format(line) - def do_help (self, line): - '''Shows This Help Screen\n''' - if line: - try: - func = getattr(self, 'help_' + line) - except AttributeError: - try: - doc = getattr(self, 'do_' + line).__doc__ - if doc: - self.stdout.write("%s\n"%str(doc)) - return - except AttributeError: - pass - self.stdout.write("%s\n"%str(self.nohelp % (line,))) - return - func() - return - - print "\nSupported Console Commands:" - print "----------------------------\n" - - cmds = [x[3:] for x in self.get_names() if x.startswith("do_")] - for cmd in cmds: - if cmd == "EOF": - continue - - try: - doc = getattr(self, 'do_' + cmd).__doc__ - if doc: - help = str(doc) - else: - help = "*** Undocumented Function ***\n" - except AttributeError: - help = "*** Undocumented Function ***\n" - - print "{:<30} {:<30}".format(cmd + " - ", help) + # def do_help (self, line): + # '''Shows This Help Screen\n''' + # if line: + # try: + # func = getattr(self, 'help_' + line) + # except AttributeError: + # try: + # doc = getattr(self, 'do_' + line).__doc__ + # if doc: + # self.stdout.write("%s\n"%str(doc)) + # return + # except AttributeError: + # pass + # self.stdout.write("%s\n"%str(self.nohelp % (line,))) + # return + # func() + # return + # + # print "\nSupported Console Commands:" + # print "----------------------------\n" + # + # cmds = [x[3:] for x in self.get_names() if x.startswith("do_")] + # for cmd in cmds: + # if cmd == "EOF": + # continue + # + # try: + # doc = getattr(self, 'do_' + cmd).__doc__ + # if doc: + # help = str(doc) + # else: + # help = "*** Undocumented Function ***\n" + # except AttributeError: + # help = "*** Undocumented Function ***\n" + # + # print "{:<30} {:<30}".format(cmd + " - ", help) def do_stream_db_add(self, line): '''Loads a YAML stream list serialization into user console \n''' @@ -594,7 +597,7 @@ class TRexConsole(cmd.Cmd): print "Provided stream list name '{0}' doesn't exists.".format(stream_pack_name) print format_text("[FAILED]\n", 'red', 'bold') return - if args[0] == "all": + if args[1] == "all": ask = ConfirmMenu('Are you sure you want to release all acquired ports? ') rc = ask.show() if rc == False: @@ -698,6 +701,33 @@ class TRexConsole(cmd.Cmd): def do_start_traffic(self, line): '''Start pre-submitted traffic in specified ports on TRex\n''' # make sure that the user wants to acquire all + # parser = parsing_opts.gen_parser("start_traffic", self.do_start_traffic.__doc__, + # parsing_opts.PORT_LIST_WITH_ALL, parsing_opts.MULTIPLIER) + # opts = parser.parse_args(line.split()) + # + # print opts + # return + # # return + # # if not opts.port_list: + # # print magenta("Please provide a list of ports separated by spaces, " + # # "or specify 'all' to start traffic on all acquired ports") + # # return + # + # if "all" in opts.port_list: + # ask = ConfirmMenu('Are you sure you want to start traffic at all acquired ports? ') + # rc = ask.show() + # if rc == False: + # print yellow("[ABORTED]\n") + # return + # else: + # port_list = self.stateless_client.get_acquired_ports() + # else: + # try: + # port_list = self.extract_port_ids_from_list(opts.port_list) + # except ValueError as e: + # print magenta(e) + # return + args = line.split() if len(args) < 1: print magenta("Please provide a list of ports separated by spaces, " @@ -715,7 +745,7 @@ class TRexConsole(cmd.Cmd): port_list = self.extract_port_ids_from_line(line) try: - res_ok, log = self.stateless_client.start_traffic(port_list) + res_ok, log = self.stateless_client.start_traffic(1.0, port_id=port_list) self.prompt_response(log) if not res_ok: print format_text("[FAILED]\n", 'red', 'bold') @@ -728,6 +758,9 @@ class TRexConsole(cmd.Cmd): def complete_start_traffic(self, text, line, begidx, endidx): return self.port_auto_complete(text, line, begidx, endidx) + def help_start_traffic(self): + self.do_start_traffic("-h") + def do_stop_traffic(self, line): '''Stop active traffic in specified ports on TRex\n''' # make sure that the user wants to acquire all -- cgit From bc7d9ee81604fd33607569ac4f03ca8b91777b29 Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 8 Nov 2015 11:39:09 +0200 Subject: code review notes: 1. performance improvement for stateless DP core object (direct object) 2. exit scheduler loop is now using a scheduled message and not IF 3. duration for inifinite is negative number 4. fixed stop_traffic scheduled node time --- scripts/automation/trex_control_plane/client/trex_stateless_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index e65a923e..627c3365 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -87,8 +87,8 @@ class CTRexStatelessClient(object): # print kwargs port_ids = kwargs.get("port_id") if not port_ids: - print "FROM ARGS!" - print args + #print "FROM ARGS!" + #print args port_ids = args[0] if isinstance(port_ids, int): # make sure port_ids is a list -- cgit From 9c32c36b6006d2a81e1a5658a1fb1616eff650f3 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 9 Nov 2015 17:10:24 +0200 Subject: moved logic to the port class also implemented the state machine for CP port --- scripts/automation/trex_control_plane/console/trex_console.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index a2c738ab..68050fc0 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -778,6 +778,9 @@ class TRexConsole(cmd.Cmd): return else: port_list = self.stateless_client.get_active_ports() + if not port_list: + print magenta("no active ports - operation aborted\n") + return else: port_list = self.extract_port_ids_from_line(line) -- cgit From d04fb533c0843ebcd3eac5fbefa6f418582db7fc Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 10 Nov 2015 10:16:28 +0200 Subject: Major progress in parsing, not stable yet Most advanced: start, stop functionality --- .../client/trex_stateless_client.py | 31 +- .../trex_control_plane/console/parsing_opts.py | 92 +++- .../trex_control_plane/console/trex_console.py | 495 ++++++++++++++++----- 3 files changed, 488 insertions(+), 130 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 627c3365..11728965 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -51,10 +51,10 @@ class CTRexStatelessClient(object): # print args # print kwargs port_ids = kwargs.get("port_id") - if not port_ids: - # print "FROM ARGS!" - # print args - port_ids = args[0] + # if not port_ids: + # # print "FROM ARGS!" + # # print args + # port_ids = args[0] if isinstance(port_ids, int): # make sure port_ids is a list port_ids = [port_ids] @@ -74,8 +74,8 @@ class CTRexStatelessClient(object): continue if bad_ids: # Some port IDs are not according to desires status - raise ValueError("The requested method ('{0}') cannot be invoked since port IDs {1} are not " - "at allowed states".format(func.__name__, list(bad_ids))) + raise ValueError("The requested method ('{0}') cannot be invoked since port IDs {1} aren't " + "acquired".format(func.__name__, list(bad_ids))) else: return func(self, *args, **kwargs) return wrapper_f @@ -232,7 +232,8 @@ class CTRexStatelessClient(object): self.transmit(command.method, command.params), self.ack_success_test) - @force_status(owned=True) + # @force_status(owned=True) + @acquired def add_stream(self, stream_id, stream_obj, port_id=None): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") @@ -243,15 +244,16 @@ class CTRexStatelessClient(object): "stream": stream_obj.dump()} return self.transmit("add_stream", params) - @force_status(owned=True) - def add_stream_pack(self, port_id=None, *stream_packs): + # @force_status(owned=True) + @acquired + def add_stream_pack(self, stream_pack_list, port_id=None): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") # since almost every run contains more than one transaction with server, handle all as batch mode port_ids = set(port_id) # convert to set to avoid duplications commands = [] - for stream_pack in stream_packs: + for stream_pack in stream_pack_list: commands.extend([RpcCmdData("add_stream", {"port_id": p_id, "handler": self._conn_handler.get(p_id), "stream_id": stream_pack.stream_id, @@ -273,7 +275,8 @@ class CTRexStatelessClient(object): "stream_id": stream_id} return self.transmit("remove_stream", params) - @force_status(owned=True) + # @force_status(owned=True) + @acquired def remove_all_streams(self, port_id=None): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") @@ -347,13 +350,17 @@ class CTRexStatelessClient(object): self.transmit(command.method, command.params), self.ack_success_test) - @force_status(owned=False, active_and_owned=True) + # @force_status(owned=False, active_and_owned=True) + @acquired def stop_traffic(self, port_id=None): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications + if not port_ids: + # don't invoke if port ids is empty + return True, [] commands = [RpcCmdData("stop_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index c94a7461..e701b7db 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -1,6 +1,7 @@ import argparse from collections import namedtuple import sys +import re ArgumentPack = namedtuple('ArgumentPack', ['name_or_flags', 'options']) ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options']) @@ -14,29 +15,88 @@ PORT_LIST_WITH_ALL = 4 FILE_PATH = 5 FILE_FROM_DB = 6 STREAM_FROM_PATH_OR_FILE = 7 +DURATION = 8 +FORCE = 9 # list of ArgumentGroup types MUTEX = 1 +def match_time_unit(val): + '''match some val against time shortcut inputs ''' + match = re.match("^(\d+)([m|h]?)$", val) + if match: + digit = int(match.group(1)) + unit = match.group(2) + if not unit: + return digit + elif unit == 'm': + return digit*60 + else: + return digit*60*60 + else: + raise argparse.ArgumentTypeError("Duration should be passed in the following format: \n" + "-d 100 : in sec \n" + "-d 10m : in min \n" + "-d 1h : in hours") + +def match_multiplier(val): + '''match some val against multiplier shortcut inputs ''' + match = re.match("^(\d+)(gb|kpps|%?)$", val) + if match: + digit = int(match.group(1)) + unit = match.group(2) + if not unit: + return digit + elif unit == 'gb': + raise NotImplementedError("gb units are not supported yet") + else: + raise NotImplementedError("kpps units are not supported yet") + else: + raise argparse.ArgumentTypeError("Multiplier should be passed in the following format: \n" + "-m 100 : multiply stream file by this factor \n" + "-m 10gb : from graph calculate the maximum rate as this bandwidth (for each port)\n" + "-m 10kpps : from graph calculate the maximum rate as this pps (for each port)\n" + "-m 40% : from graph calculate the maximum rate as this percent from total port (for each port)") + + OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], - {'help': "Set multiplier for stream", 'dest': "mult", 'type': float}), + {'help': "Set multiplier for stream", + 'dest': "mult", + 'default': 1.0, + 'type': match_multiplier}), PORT_LIST: ArgumentPack(['--port'], {"nargs": '+', # "action": "store_" + 'dest':'ports', + 'metavar': 'PORTS', + # 'type': int, 'help': "A list of ports on which to apply the command", 'default': []}), ALL_PORTS: ArgumentPack(['-a'], {"action": "store_true", - "dest": "all", + "dest": "all_ports", 'help': "Set this flag to apply the command on all available ports"}), - + DURATION: ArgumentPack(['-d'], + {"action": "store", + 'metavar': 'TIME', + "type": match_time_unit, + 'help': "Set duration time for TRex."}), + FORCE: ArgumentPack(['--force'], + {"action": "store_true", + 'default': False, + 'help': "Set if you want to stop active ports before applying new TRex run on them."}), FILE_PATH: ArgumentPack(['-f'], - {'help': "File path to YAML file that describes a stream pack"}), + {'metavar': ('FILE', 'DB_NAME'), + 'dest': 'file', + 'nargs': 2, + 'help': "File path to YAML file that describes a stream pack. " + "Second argument is a name to store the loaded yaml file into db."}), FILE_FROM_DB: ArgumentPack(['--db'], - {'help': "A stream pack which already loaded into console cache."}), + {'metavar': 'LOADED_STREAM_PACK', + 'help': "A stream pack which already loaded into console cache."}), # advanced options PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST, ALL_PORTS], @@ -51,25 +111,23 @@ class CCmdArgParser(argparse.ArgumentParser): def __init__(self, *args, **kwargs): super(CCmdArgParser, self).__init__(*args, **kwargs) - pass - # def error(self, message): + # def exit(self, status=0, message=None): # try: - # super(CCmdArgParser, self).error(message) # this will trigger system exit! + # return super(CCmdArgParser, self).exit(status, message) # this will trigger system exit! # except SystemExit: + # print "Caught system exit!!" # return -1 - # - # # self.print_usage(sys.stderr) - # # print ('%s: error: %s\n') % (self.prog, message) - # # self.print_help() - # return + # # return - def exit(self, status=0, message=None): + def parse_args(self, args=None, namespace=None): try: - super(CCmdArgParser, self).exit(status, message) # this will trigger system exit! + return super(CCmdArgParser, self).parse_args(args, namespace) except SystemExit: - return -1 - return + # recover from system exit scenarios, such as "help", or bad arguments. + return None + + def gen_parser(op_name, description, *args): parser = CCmdArgParser(prog=op_name, conflict_handler='resolve', diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 68050fc0..a61881a1 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -163,6 +163,7 @@ class TRexConsole(cmd.Cmd): self.intro += "\nType 'help' or '?' for supported actions\n" self.verbose = False + self._silent = True self.postcmd(False, "") @@ -177,7 +178,7 @@ class TRexConsole(cmd.Cmd): # set verbose on / off - def do_verbose (self, line): + def do_verbose(self, line): '''Shows or set verbose mode\n''' if line == "": print "\nverbose is " + ("on\n" if self.verbose else "off\n") @@ -211,7 +212,7 @@ class TRexConsole(cmd.Cmd): print format_text("[SUCCESS]\n", 'green', 'bold') return - def do_ping (self, line): + def do_ping(self, line): '''Pings the RPC server\n''' print "\n-> Pinging RPC server" @@ -223,7 +224,7 @@ class TRexConsole(cmd.Cmd): print "\n*** " + msg + "\n" return - def do_force_acquire (self, line): + def do_force_acquire(self, line): '''Acquires ports by force\n''' self.do_acquire(line, True) @@ -367,49 +368,49 @@ class TRexConsole(cmd.Cmd): self.supported_rpc = self.stateless_client.get_supported_cmds().data - def do_rpc (self, line): - '''Launches a RPC on the server\n''' - - if line == "": - print "\nUsage: [method name] [param dict as string]\n" - print "Example: rpc test_add {'x': 12, 'y': 17}\n" - return - - sp = line.split(' ', 1) - method = sp[0] - - params = None - bad_parse = False - if len(sp) > 1: - - try: - params = ast.literal_eval(sp[1]) - if not isinstance(params, dict): - bad_parse = True - - except ValueError as e1: - bad_parse = True - except SyntaxError as e2: - bad_parse = True - - if bad_parse: - print "\nValue should be a valid dict: '{0}'".format(sp[1]) - print "\nUsage: [method name] [param dict as string]\n" - print "Example: rpc test_add {'x': 12, 'y': 17}\n" - return - - res_ok, msg = self.stateless_client.transmit(method, params) - if res_ok: - print "\nServer Response:\n\n" + pretty_json(json.dumps(msg)) + "\n" - else: - print "\n*** " + msg + "\n" - #print "Please try 'reconnect' to reconnect to server" - - - def complete_rpc (self, text, line, begidx, endidx): - return [x - for x in self.supported_rpc - if x.startswith(text)] + # def do_rpc (self, line): + # '''Launches a RPC on the server\n''' + # + # if line == "": + # print "\nUsage: [method name] [param dict as string]\n" + # print "Example: rpc test_add {'x': 12, 'y': 17}\n" + # return + # + # sp = line.split(' ', 1) + # method = sp[0] + # + # params = None + # bad_parse = False + # if len(sp) > 1: + # + # try: + # params = ast.literal_eval(sp[1]) + # if not isinstance(params, dict): + # bad_parse = True + # + # except ValueError as e1: + # bad_parse = True + # except SyntaxError as e2: + # bad_parse = True + # + # if bad_parse: + # print "\nValue should be a valid dict: '{0}'".format(sp[1]) + # print "\nUsage: [method name] [param dict as string]\n" + # print "Example: rpc test_add {'x': 12, 'y': 17}\n" + # return + # + # res_ok, msg = self.stateless_client.transmit(method, params) + # if res_ok: + # print "\nServer Response:\n\n" + pretty_json(json.dumps(msg)) + "\n" + # else: + # print "\n*** " + msg + "\n" + # #print "Please try 'reconnect' to reconnect to server" + # + # + # def complete_rpc (self, text, line, begidx, endidx): + # return [x + # for x in self.supported_rpc + # if x.startswith(text)] def do_status (self, line): '''Shows a graphical console\n''' @@ -611,7 +612,7 @@ class TRexConsole(cmd.Cmd): owned = set(self.stateless_client.get_acquired_ports()) try: if set(port_list).issubset(owned): - res_ok, log = self.stateless_client.add_stream_pack(port_list, *stream_list.compiled) + res_ok, log = self.stateless_client.add_stream_pack(stream_list.compiled, port_id=port_list) # res_ok, msg = self.stateless_client.add_stream(port_list, stream_list.compiled) self.prompt_response(log) if not res_ok: @@ -699,14 +700,302 @@ class TRexConsole(cmd.Cmd): def complete_remove_all_streams(self, text, line, begidx, endidx): return self.port_auto_complete(text, line, begidx, endidx) + def do_start(self, line): + '''Start selected traffic in specified ports on TRex\n''' + # make sure that the user wants to acquire all + parser = parsing_opts.gen_parser("start", self.do_start.__doc__, + parsing_opts.PORT_LIST_WITH_ALL, + parsing_opts.FORCE, + parsing_opts.STREAM_FROM_PATH_OR_FILE, + parsing_opts.DURATION, + parsing_opts.MULTIPLIER) + opts = parser.parse_args(line.split()) + if opts is None: + # avoid further processing in this command + return + # print opts + port_list = self.extract_port_list(opts) + # print port_list + if opts.force: + # stop all active ports, if any + res_ok = self.stop_traffic(set(self.stateless_client.get_active_ports()).intersection(port_list)) + if not res_ok: + print yellow("[ABORTED]\n") + return + # remove all traffic from ports + res_ok = self.remove_all_streams(port_list) + if not res_ok: + print yellow("[ABORTED]\n") + return + # decide which traffic to use + stream_pack_name = None + if opts.db: + # use pre-loaded traffic + print format_text('{:<30}'.format("Load stream pack (from DB):"), 'bold'), + if opts.db not in self.streams_db.get_loaded_streams_names(): + print format_text("[FAILED]\n", 'red', 'bold') + print yellow("[ABORTED]\n") + return + else: + stream_pack_name = opts.db + else: + # try loading a YAML file + print format_text('{:<30}'.format("Load stream pack (from file):"), 'bold'), + stream_list = CStreamList() + loaded_obj = stream_list.load_yaml(opts.file[0]) + # print self.stateless_client.pretty_json(json.dumps(loaded_obj)) + try: + compiled_streams = stream_list.compile_streams() + res_ok = self.streams_db.load_streams(opts.file[1], + LoadedStreamList(loaded_obj, + [StreamPack(v.stream_id, v.stream.dump()) + for k, v in compiled_streams.items()])) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + print yellow("[ABORTED]\n") + return + print format_text("[SUCCESS]\n", 'green', 'bold') + stream_pack_name = opts.file[1] + except Exception as e: + print format_text("[FAILED]\n", 'red', 'bold') + print yellow("[ABORTED]\n") + res_ok = self.attach_to_port(stream_pack_name, port_list) + if not res_ok: + print yellow("[ABORTED]\n") + return + # finally, start the traffic + res_ok = self.start_traffic(opts.mult, port_list) + if not res_ok: + print yellow("[ABORTED]\n") + return + return + + def help_start(self): + self.do_start("-h") + + def do_stop(self, line): + '''Stop active traffic in specified ports on TRex\n''' + parser = parsing_opts.gen_parser("stop", self.do_stop.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + opts = parser.parse_args(line.split()) + if opts is None: + # avoid further processing in this command + return + port_list = self.extract_port_list(opts) + res_ok = self.stop_traffic(port_list) + return + + + def help_stop(self): + self.do_stop("-h") + + + def do_debug(self, line): + '''Enter DEBUG mode of the console to invoke smaller building blocks with server''' + i = DebugTRexConsole(self) + i.prompt = self.prompt[:-3] + ':' + blue('debug') + ' > ' + i.cmdloop() + + # aliasing + do_exit = do_EOF = do_q = do_quit + + # ----- utility methods ----- # + + def start_traffic(self, multiplier, port_list):#, silent=True): + print format_text('{:<30}'.format("Start traffic:"), 'bold'), + try: + res_ok, log = self.stateless_client.start_traffic(multiplier, port_id=port_list) + if not self._silent: + print '' + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return False + print format_text("[SUCCESS]\n", 'green', 'bold') + return True + except ValueError as e: + print '' + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + return False + + def attach_to_port(self, stream_pack_name, port_list): + print format_text('{:<30}'.format("Attaching traffic to ports:"), 'bold'), + stream_list = self.streams_db.get_stream_pack(stream_pack_name) #user_streams[args[0]] + if not stream_list: + print "Provided stream list name '{0}' doesn't exists.".format(stream_pack_name) + print format_text("[FAILED]\n", 'red', 'bold') + return + try: + res_ok, log = self.stateless_client.add_stream_pack(stream_list.compiled, port_id=port_list) + if not self._silent: + print '' + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return False + print format_text("[SUCCESS]\n", 'green', 'bold') + return True + except ValueError as e: + print '' + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + return False + + def stop_traffic(self, port_list): + print format_text('{:<30}'.format("Stop traffic:"), 'bold'), + try: + res_ok, log = self.stateless_client.stop_traffic(port_id=port_list) + if not self._silent: + print '' + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + return True + except ValueError as e: + print '' + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + + def remove_all_streams(self, port_list): + '''Remove all streams from given port_list''' + print format_text('{:<30}'.format("Remove all streams:"), 'bold'), + try: + res_ok, log = self.stateless_client.remove_all_streams(port_id=port_list) + if not self._silent: + print '' + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + return True + except ValueError as e: + print '' + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + + + + + + def extract_port_list(self, opts): + if opts.all_ports or "all" in opts.ports: + # handling all ports + port_list = self.stateless_client.get_acquired_ports() + else: + port_list = self.extract_port_ids_from_list(opts.ports) + return port_list + + def decode_multiplier(self, opts_mult): + pass + + +class DebugTRexConsole(cmd.Cmd): + + def __init__(self, trex_main_console): + cmd.Cmd.__init__(self) + self.trex_console = trex_main_console + self.stateless_client = self.trex_console.stateless_client + self.streams_db = self.trex_console.streams_db + self.register_main_console_methods() + self.do_silent("on") + pass + + # ----- super methods overriding ----- # + def completenames(self, text, *ignored): + dotext = 'do_'+text + return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)] + + def get_names(self): + result = cmd.Cmd.get_names(self) + result += self.trex_console.get_names() + return list(set(result)) + + def register_main_console_methods(self): + main_names = set(self.trex_console.get_names()).difference(set(dir(self.__class__))) + for name in main_names: + for prefix in 'do_', 'help_', 'complete_': + if name.startswith(prefix): + self.__dict__[name] = getattr(self.trex_console, name) + + # if (name[:3] == 'do_') or (name[:5] == 'help_') or (name[:9] == 'complete_'): + # chosen.append(name) + # self.__dict__[name] = getattr(self.trex_console, name) + # # setattr(self, name, classmethod(getattr(self.trex_console, name))) + + # print chosen + # self.get_names() + + # return result + + + # ----- DEBUGGING methods ----- # + # set silent on / off + def do_silent(self, line): + '''Shows or set silent mode\n''' + if line == "": + print "\nsilent mode is " + ("on\n" if self.trex_console._silent else "off\n") + + elif line == "on": + self.verbose = True + self.stateless_client.set_verbose(True) + print green("\nsilent set to on\n") + + elif line == "off": + self.verbose = False + self.stateless_client.set_verbose(False) + print green("\nsilent set to off\n") + + else: + print magenta("\nplease specify 'on' or 'off'\n") + + def do_quit(self, line): + '''Exit the debug client back to main console\n''' + self.do_silent("off") + return True + def do_start_traffic(self, line): '''Start pre-submitted traffic in specified ports on TRex\n''' # make sure that the user wants to acquire all - # parser = parsing_opts.gen_parser("start_traffic", self.do_start_traffic.__doc__, - # parsing_opts.PORT_LIST_WITH_ALL, parsing_opts.MULTIPLIER) - # opts = parser.parse_args(line.split()) - # + parser = parsing_opts.gen_parser("start_traffic", self.do_start_traffic.__doc__, + parsing_opts.PORT_LIST_WITH_ALL, parsing_opts.MULTIPLIER) + opts = parser.parse_args(line.split()) # print opts + # return + if opts is None: + # avoid further processing in this command + return + try: + port_list = self.trex_console.extract_port_list(opts) + return self.trex_console.start_traffic(opts.mult, port_list) + except Exception as e: + print e + return + + def do_stop_traffic(self, line): + '''Stop active traffic in specified ports on TRex\n''' + parser = parsing_opts.gen_parser("stop_traffic", self.do_stop_traffic.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + opts = parser.parse_args(line.split()) + # print opts + # return + if opts is None: + # avoid further processing in this command + return + try: + port_list = self.trex_console.extract_port_list(opts) + return self.trex_console.stop_traffic(port_list) + except Exception as e: + print e + return + + + def complete_stop_traffic(self, text, line, begidx, endidx): + return self.port_auto_complete(text, line, begidx, endidx, active=True) + # return # # return # # if not opts.port_list: @@ -714,21 +1003,9 @@ class TRexConsole(cmd.Cmd): # # "or specify 'all' to start traffic on all acquired ports") # # return # - # if "all" in opts.port_list: - # ask = ConfirmMenu('Are you sure you want to start traffic at all acquired ports? ') - # rc = ask.show() - # if rc == False: - # print yellow("[ABORTED]\n") - # return - # else: - # port_list = self.stateless_client.get_acquired_ports() - # else: - # try: - # port_list = self.extract_port_ids_from_list(opts.port_list) - # except ValueError as e: - # print magenta(e) - # return + + return args = line.split() if len(args) < 1: print magenta("Please provide a list of ports separated by spaces, " @@ -757,46 +1034,60 @@ class TRexConsole(cmd.Cmd): print format_text("[FAILED]\n", 'red', 'bold') def complete_start_traffic(self, text, line, begidx, endidx): - return self.port_auto_complete(text, line, begidx, endidx) + # return self.port_auto_complete(text, line, begidx, endidx) + return [text] def help_start_traffic(self): self.do_start_traffic("-h") - def do_stop_traffic(self, line): - '''Stop active traffic in specified ports on TRex\n''' - # make sure that the user wants to acquire all - args = line.split() - if len(args) < 1: - print magenta("Please provide a list of ports separated by spaces, " - "or specify 'all' to stop traffic on all acquired ports") + def help_stop_traffic(self): + self.do_stop_traffic("-h") + + # def do_help(self): + + def do_rpc (self, line): + '''Launches a RPC on the server\n''' + + if line == "": + print "\nUsage: [method name] [param dict as string]\n" + print "Example: rpc test_add {'x': 12, 'y': 17}\n" return - if args[0] == "all": - ask = ConfirmMenu('Are you sure you want to start traffic at all acquired ports? ') - rc = ask.show() - if rc == False: - print yellow("[ABORTED]\n") - return - else: - port_list = self.stateless_client.get_active_ports() - if not port_list: - print magenta("no active ports - operation aborted\n") - return + + sp = line.split(' ', 1) + method = sp[0] + + params = None + bad_parse = False + if len(sp) > 1: + + try: + params = ast.literal_eval(sp[1]) + if not isinstance(params, dict): + bad_parse = True + + except ValueError as e1: + bad_parse = True + except SyntaxError as e2: + bad_parse = True + + if bad_parse: + print "\nValue should be a valid dict: '{0}'".format(sp[1]) + print "\nUsage: [method name] [param dict as string]\n" + print "Example: rpc test_add {'x': 12, 'y': 17}\n" + return + + res_ok, msg = self.stateless_client.transmit(method, params) + if res_ok: + print "\nServer Response:\n\n" + pretty_json(json.dumps(msg)) + "\n" else: - port_list = self.extract_port_ids_from_line(line) + print "\n*** " + msg + "\n" + #print "Please try 'reconnect' to reconnect to server" - try: - res_ok, log = self.stateless_client.stop_traffic(port_list) - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - except ValueError as e: - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - def complete_stop_traffic(self, text, line, begidx, endidx): - return self.port_auto_complete(text, line, begidx, endidx, active=True) + def complete_rpc (self, text, line, begidx, endidx): + return [x + for x in self.trex_console.supported_rpc + if x.startswith(text)] # aliasing do_exit = do_EOF = do_q = do_quit @@ -808,12 +1099,13 @@ def setParserOptions(): default = "localhost", type = str) - parser.add_argument("-p", "--port", help = "TRex Server Port [default is 5050]\n", - default = 5050, + parser.add_argument("-p", "--port", help = "TRex Server Port [default is 4505]\n", + default = 4505, type = int) - parser.add_argument("-z", "--pub", help = "TRex Async Publisher Port [default is 4500]\n", - default = 4500, + parser.add_argument("--async_port", help = "TRex ASync Publisher Port [default is 4506]\n", + default = 4506, + dest='pub', type = int) parser.add_argument("-u", "--user", help = "User Name [default is currently logged in user]\n", @@ -826,6 +1118,7 @@ def setParserOptions(): return parser + def main(): parser = setParserOptions() options = parser.parse_args() -- cgit From 467382a7611f478d66cf58c1307f079239f7bac6 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 11 Nov 2015 13:48:02 +0200 Subject: lightweight console --- .../client/trex_stateless_client.py | 102 +- .../trex_control_plane/console/old_console.py | 946 +++++++++++++++++ .../trex_control_plane/console/parsing_opts.py | 74 +- .../trex_control_plane/console/trex_console.py | 1111 ++++---------------- .../trex_control_plane/console/trex_status.py | 5 +- 5 files changed, 1284 insertions(+), 954 deletions(-) create mode 100644 scripts/automation/trex_control_plane/console/old_console.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 11728965..168853b3 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -232,7 +232,6 @@ class CTRexStatelessClient(object): self.transmit(command.method, command.params), self.ack_success_test) - # @force_status(owned=True) @acquired def add_stream(self, stream_id, stream_obj, port_id=None): if not self._is_ports_valid(port_id): @@ -244,7 +243,6 @@ class CTRexStatelessClient(object): "stream": stream_obj.dump()} return self.transmit("add_stream", params) - # @force_status(owned=True) @acquired def add_stream_pack(self, stream_pack_list, port_id=None): if not self._is_ports_valid(port_id): @@ -262,9 +260,11 @@ class CTRexStatelessClient(object): for p_id in port_ids] ) res_ok, resp_list = self.transmit_batch(commands) - if res_ok: - return self._process_batch_result(commands, resp_list, self._handle_add_stream_response, - success_test=self.ack_success_test) + if not res_ok: + return res_ok, resp_list + + return self._process_batch_result(commands, resp_list, self._handle_add_stream_response, + success_test=self.ack_success_test) @force_status(owned=True) def remove_stream(self, stream_id, port_id=None): @@ -275,8 +275,6 @@ class CTRexStatelessClient(object): "stream_id": stream_id} return self.transmit("remove_stream", params) - # @force_status(owned=True) - @acquired def remove_all_streams(self, port_id=None): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") @@ -326,7 +324,6 @@ class CTRexStatelessClient(object): "get_pkt": get_pkt} return self.transmit("get_stream_list", params) - @acquired def start_traffic(self, multiplier, port_id=None): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") @@ -350,8 +347,6 @@ class CTRexStatelessClient(object): self.transmit(command.method, command.params), self.ack_success_test) - # @force_status(owned=False, active_and_owned=True) - @acquired def stop_traffic(self, port_id=None): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") @@ -371,7 +366,7 @@ class CTRexStatelessClient(object): params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} command = RpcCmdData("stop_traffic", params) - return self._handle_start_traffic_response(command, + return self._handle_stop_traffic_response(command, self.transmit(command.method, command.params), self.ack_success_test) @@ -471,6 +466,88 @@ class CTRexStatelessClient(object): return False + ######################### Console (high level) API ######################### + + # reset + # acquire, stop, remove streams and clear stats + # + # + def cmd_reset (self, annotate_func): + + ports = self.get_port_ids() + + # sync with the server + rc, log = self._init_sync() + annotate_func("Syncing with the server:", rc, log) + if not rc: + return False + + + # force acquire all ports + rc, log = self.acquire(ports, force = True) + annotate_func("Force acquiring all ports:", rc, log) + if not rc: + return False + + # force stop + rc, log = self.stop_traffic(ports) + annotate_func("Stop traffic on all ports:", rc, log) + if not rc: + return False + + # remove all streams + rc, log = self.remove_all_streams(ports) + annotate_func("Removing all streams from all ports:", rc, log) + if not rc: + return False + + # TODO: clear stats + return True + + + # stop cmd + def cmd_stop (self, ports, annotate_func): + + # find the relveant ports + active_ports = set(self.get_active_ports()).intersection(ports) + if not active_ports: + annotate_func("No active traffic on porivded ports") + return True + + rc, log = self.stop_traffic(active_ports) + annotate_func("Stopping traffic on ports {0}:".format([port for port in active_ports]), rc, log) + if not rc: + return False + + return True + + # start cmd + def cmd_start (self, ports, stream_list, mult, force, annotate_func): + + if force: + rc = self.cmd_stop(ports, annotate_func) + if not rc: + return False + + rc, log = self.remove_all_streams(ports) + annotate_func("Removing all streams from ports {0}:".format([port for port in ports]), rc, log, + "Please either retry with --force or stop traffic") + if not rc: + return False + + rc, log = self.add_stream_pack(stream_list.compiled, port_id= ports) + annotate_func("Attaching streams to port {0}:".format([port for port in ports]), rc, log) + if not rc: + return False + + # finally, start the traffic + rc, log = self.start_traffic(mult, ports) + annotate_func("Starting traffic on ports {0}:".format([port for port in ports]), rc, log) + if not rc: + return False + + return True + # ----- handler internal methods ----- # def _handle_general_response(self, request, response, msg, success_test=None): port_id = request.params.get("port_id") @@ -525,7 +602,8 @@ class CTRexStatelessClient(object): def _handle_stop_traffic_response(self, request, response, success_test): port_id = request.params.get("port_id") if success_test(response): - self._active_ports.remove(port_id) + if port_id in self._active_ports: + self._active_ports.remove(port_id) return RpcResponseStatus(True, port_id, "Traffic stopped") else: return RpcResponseStatus(False, port_id, response.data) diff --git a/scripts/automation/trex_control_plane/console/old_console.py b/scripts/automation/trex_control_plane/console/old_console.py new file mode 100644 index 00000000..93c7e3f4 --- /dev/null +++ b/scripts/automation/trex_control_plane/console/old_console.py @@ -0,0 +1,946 @@ + +# main console object +class TRexConsole1(cmd.Cmd): + """Trex Console""" + + def __init__(self, stateless_client, verbose): + cmd.Cmd.__init__(self) + + self.stateless_client = stateless_client + + self.do_connect("") + + self.intro = "\n-=TRex Console v{ver}=-\n".format(ver=__version__) + self.intro += "\nType 'help' or '?' for supported actions\n" + + self.verbose = False + self._silent = True + + self.postcmd(False, "") + + self.user_streams = {} + self.streams_db = CStreamsDB() + + + # a cool hack - i stole this function and added space + def completenames(self, text, *ignored): + dotext = 'do_'+text + return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)] + + + # set verbose on / off + def do_verbose(self, line): + '''Shows or set verbose mode\n''' + if line == "": + print "\nverbose is " + ("on\n" if self.verbose else "off\n") + + elif line == "on": + self.verbose = True + self.stateless_client.set_verbose(True) + print green("\nverbose set to on\n") + + elif line == "off": + self.verbose = False + self.stateless_client.set_verbose(False) + print green("\nverbose set to off\n") + + else: + print magenta("\nplease specify 'on' or 'off'\n") + + # query the server for registered commands + def do_query_server(self, line): + '''query the RPC server for supported remote commands\n''' + + res_ok, msg = self.stateless_client.get_supported_cmds() + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print "\nRPC server supports the following commands:\n" + for func in msg: + if func: + print func + print '' + print format_text("[SUCCESS]\n", 'green', 'bold') + return + + def do_ping(self, line): + '''Pings the RPC server\n''' + + print "\n-> Pinging RPC server" + + res_ok, msg = self.stateless_client.ping() + if res_ok: + print format_text("[SUCCESS]\n", 'green', 'bold') + else: + print "\n*** " + msg + "\n" + return + + def do_force_acquire(self, line): + '''Acquires ports by force\n''' + + self.do_acquire(line, True) + + def complete_force_acquire(self, text, line, begidx, endidx): + return self.port_auto_complete(text, line, begidx, endidx, acquired=False) + + def extract_port_ids_from_line(self, line): + return {int(x) for x in line.split()} + + def extract_port_ids_from_list(self, port_list): + return {int(x) for x in port_list} + + def parse_ports_from_line (self, line): + port_list = set() + if line: + for port_id in line.split(' '): + if (not port_id.isdigit()) or (int(port_id) < 0) or (int(port_id) >= self.stateless_client.get_port_count()): + print "Please provide a list of ports separated by spaces between 0 and {0}".format(self.stateless_client.get_port_count() - 1) + return None + + port_list.add(int(port_id)) + + port_list = list(port_list) + + else: + port_list = [i for i in xrange(0, self.stateless_client.get_port_count())] + + return port_list + + + def do_acquire(self, line, force=False): + '''Acquire ports\n''' + + # make sure that the user wants to acquire all + args = line.split() + if len(args) < 1: + print magenta("Please provide a list of ports separated by spaces, or specify 'all' to acquire all available ports") + return + + if args[0] == "all": + ask = ConfirmMenu('Are you sure you want to acquire all ports ? ') + rc = ask.show() + if rc == False: + print yellow("[ABORTED]\n") + return + else: + port_list = self.stateless_client.get_port_ids() + else: + port_list = self.extract_port_ids_from_line(line) + + # rc, resp_list = self.stateless_client.take_ownership(port_list, force) + try: + res_ok, log = self.stateless_client.acquire(port_list, force) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + except ValueError as e: + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + + + def port_auto_complete(self, text, line, begidx, endidx, acquired=True, active=False): + if acquired: + if not active: + ret_list = [x + for x in map(str, self.stateless_client.get_acquired_ports()) + if x.startswith(text)] + else: + ret_list = [x + for x in map(str, self.stateless_client.get_active_ports()) + if x.startswith(text)] + else: + ret_list = [x + for x in map(str, self.stateless_client.get_port_ids()) + if x.startswith(text)] + ret_list.append("all") + return ret_list + + + def complete_acquire(self, text, line, begidx, endidx): + return self.port_auto_complete(text, line, begidx, endidx, acquired=False) + + def do_release (self, line): + '''Release ports\n''' + + # if line: + # port_list = self.parse_ports_from_line(line) + # else: + # port_list = self.stateless_client.get_owned_ports() + args = line.split() + if len(args) < 1: + print "Please provide a list of ports separated by spaces, or specify 'all' to acquire all available ports" + if args[0] == "all": + ask = ConfirmMenu('Are you sure you want to release all acquired ports? ') + rc = ask.show() + if rc == False: + print yellow("[ABORTED]\n") + return + else: + port_list = self.stateless_client.get_acquired_ports() + else: + port_list = self.extract_port_ids_from_line(line) + + try: + res_ok, log = self.stateless_client.release(port_list) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + except ValueError as e: + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + return + + def complete_release(self, text, line, begidx, endidx): + return self.port_auto_complete(text, line, begidx, endidx) + + def do_connect (self, line): + '''Connects to the server\n''' + + if line == "": + res_ok, msg = self.stateless_client.connect() + else: + sp = line.split() + if (len(sp) != 2): + print "\n[usage] connect [server] [port] or without parameters\n" + return + + res_ok, msg = self.stateless_client.connect(sp[0], sp[1]) + + if res_ok: + print format_text("[SUCCESS]\n", 'green', 'bold') + else: + print "\n*** " + msg + "\n" + print format_text("[FAILED]\n", 'red', 'bold') + return + + self.supported_rpc = self.stateless_client.get_supported_cmds().data + + # def do_rpc (self, line): + # '''Launches a RPC on the server\n''' + # + # if line == "": + # print "\nUsage: [method name] [param dict as string]\n" + # print "Example: rpc test_add {'x': 12, 'y': 17}\n" + # return + # + # sp = line.split(' ', 1) + # method = sp[0] + # + # params = None + # bad_parse = False + # if len(sp) > 1: + # + # try: + # params = ast.literal_eval(sp[1]) + # if not isinstance(params, dict): + # bad_parse = True + # + # except ValueError as e1: + # bad_parse = True + # except SyntaxError as e2: + # bad_parse = True + # + # if bad_parse: + # print "\nValue should be a valid dict: '{0}'".format(sp[1]) + # print "\nUsage: [method name] [param dict as string]\n" + # print "Example: rpc test_add {'x': 12, 'y': 17}\n" + # return + # + # res_ok, msg = self.stateless_client.transmit(method, params) + # if res_ok: + # print "\nServer Response:\n\n" + pretty_json(json.dumps(msg)) + "\n" + # else: + # print "\n*** " + msg + "\n" + # #print "Please try 'reconnect' to reconnect to server" + # + # + # def complete_rpc (self, text, line, begidx, endidx): + # return [x + # for x in self.supported_rpc + # if x.startswith(text)] + + def do_status (self, line): + '''Shows a graphical console\n''' + + if not self.stateless_client.is_connected(): + print "Not connected to server\n" + return + + self.do_verbose('off') + trex_status.show_trex_status(self.stateless_client) + + def do_quit(self, line): + '''Exit the client\n''' + return True + + def do_disconnect (self, line): + '''Disconnect from the server\n''' + if not self.stateless_client.is_connected(): + print "Not connected to server\n" + return + + res_ok, msg = self.stateless_client.disconnect() + if res_ok: + print format_text("[SUCCESS]\n", 'green', 'bold') + else: + print msg + "\n" + + def do_whoami (self, line): + '''Prints console user name\n''' + print "\n" + self.stateless_client.user + "\n" + + def postcmd(self, stop, line): + if self.stateless_client.is_connected(): + self.prompt = "TRex > " + else: + self.supported_rpc = None + self.prompt = "TRex (offline) > " + + return stop + + def default(self, line): + print "'{0}' is an unrecognized command. type 'help' or '?' for a list\n".format(line) + + # def do_help (self, line): + # '''Shows This Help Screen\n''' + # if line: + # try: + # func = getattr(self, 'help_' + line) + # except AttributeError: + # try: + # doc = getattr(self, 'do_' + line).__doc__ + # if doc: + # self.stdout.write("%s\n"%str(doc)) + # return + # except AttributeError: + # pass + # self.stdout.write("%s\n"%str(self.nohelp % (line,))) + # return + # func() + # return + # + # print "\nSupported Console Commands:" + # print "----------------------------\n" + # + # cmds = [x[3:] for x in self.get_names() if x.startswith("do_")] + # for cmd in cmds: + # if cmd == "EOF": + # continue + # + # try: + # doc = getattr(self, 'do_' + cmd).__doc__ + # if doc: + # help = str(doc) + # else: + # help = "*** Undocumented Function ***\n" + # except AttributeError: + # help = "*** Undocumented Function ***\n" + # + # print "{:<30} {:<30}".format(cmd + " - ", help) + + def do_stream_db_add(self, line): + '''Loads a YAML stream list serialization into user console \n''' + args = line.split() + if len(args) >= 2: + name = args[0] + yaml_path = args[1] + try: + multiplier = args[2] + except IndexError: + multiplier = 1 + stream_list = CStreamList() + loaded_obj = stream_list.load_yaml(yaml_path, multiplier) + # print self.stateless_client.pretty_json(json.dumps(loaded_obj)) + try: + compiled_streams = stream_list.compile_streams() + res_ok = self.streams_db.load_streams(name, LoadedStreamList(loaded_obj, + [StreamPack(v.stream_id, v.stream.dump()) + for k, v in compiled_streams.items()])) + if res_ok: + print green("Stream pack '{0}' loaded and added successfully\n".format(name)) + else: + print magenta("Picked name already exist. Please pick another name.\n") + except Exception as e: + print "adding new stream failed due to the following error:\n", str(e) + print format_text("[FAILED]\n", 'red', 'bold') + + return + else: + print magenta("please provide load name and YAML path, separated by space.\n" + "Optionally, you may provide a third argument to specify multiplier.\n") + + @staticmethod + def tree_autocomplete(text): + dir = os.path.dirname(text) + if dir: + path = dir + else: + path = "." + start_string = os.path.basename(text) + return [x + for x in os.listdir(path) + if x.startswith(start_string)] + + + def complete_stream_db_add(self, text, line, begidx, endidx): + arg_num = len(line.split()) - 1 + if arg_num == 2: + return TRexConsole.tree_autocomplete(line.split()[-1]) + else: + return [text] + + def do_stream_db_show(self, line): + '''Shows the loaded stream list named [name] \n''' + args = line.split() + if args: + list_name = args[0] + try: + stream = self.streams_db.get_stream_pack(list_name)#user_streams[list_name] + if len(args) >= 2 and args[1] == "full": + print pretty_json(json.dumps(stream.compiled)) + else: + print pretty_json(json.dumps(stream.loaded)) + except KeyError as e: + print "Unknown stream list name provided" + else: + print "Available stream packs:\n{0}".format(', '.join(sorted(self.streams_db.get_loaded_streams_names()))) + + def complete_stream_db_show(self, text, line, begidx, endidx): + return [x + for x in self.streams_db.get_loaded_streams_names() + if x.startswith(text)] + + def do_stream_db_remove(self, line): + '''Removes a single loaded stream packs from loaded stream pack repository\n''' + args = line.split() + if args: + removed_streams = self.streams_db.remove_stream_packs(*args) + if removed_streams: + print green("The following stream packs were removed:") + print bold(", ".join(sorted(removed_streams))) + print format_text("[SUCCESS]\n", 'green', 'bold') + else: + print red("No streams were removed. Make sure to provide valid stream pack names.") + else: + print magenta("Please provide stream pack name(s), separated with spaces.") + + def do_stream_db_clear(self, line): + '''Clears all loaded stream packs from loaded stream pack repository\n''' + self.streams_db.clear() + print format_text("[SUCCESS]\n", 'green', 'bold') + + + def complete_stream_db_remove(self, text, line, begidx, endidx): + return [x + for x in self.streams_db.get_loaded_streams_names() + if x.startswith(text)] + + + def do_attach(self, line): + '''Assign loaded stream pack into specified ports on TRex\n''' + args = line.split() + if len(args) >= 2: + stream_pack_name = args[0] + stream_list = self.streams_db.get_stream_pack(stream_pack_name) #user_streams[args[0]] + if not stream_list: + print "Provided stream list name '{0}' doesn't exists.".format(stream_pack_name) + print format_text("[FAILED]\n", 'red', 'bold') + return + if args[1] == "all": + ask = ConfirmMenu('Are you sure you want to release all acquired ports? ') + rc = ask.show() + if rc == False: + print yellow("[ABORTED]\n") + return + else: + port_list = self.stateless_client.get_acquired_ports() + else: + port_list = self.extract_port_ids_from_line(' '.join(args[1:])) + owned = set(self.stateless_client.get_acquired_ports()) + try: + if set(port_list).issubset(owned): + res_ok, log = self.stateless_client.add_stream_pack(stream_list.compiled, port_id=port_list) + # res_ok, msg = self.stateless_client.add_stream(port_list, stream_list.compiled) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + return + else: + print "Not all desired ports are acquired.\n" \ + "Acquired ports are: {acq}\n" \ + "Requested ports: {req}\n" \ + "Missing ports: {miss}".format(acq=list(owned), + req=port_list, + miss=list(set(port_list).difference(owned))) + print format_text("[FAILED]\n", 'red', 'bold') + except ValueError as e: + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + else: + print magenta("Please provide list name and ports to attach to, " + "or specify 'all' to attach all owned ports.\n") + + def complete_attach(self, text, line, begidx, endidx): + arg_num = len(line.split()) - 1 + if arg_num == 1: + # return optional streams packs + if line.endswith(" "): + return self.port_auto_complete(text, line, begidx, endidx) + return [x + for x in self.streams_db.get_loaded_streams_names() + if x.startswith(text)] + elif arg_num >= 2: + # return optional ports to attach to + return self.port_auto_complete(text, line, begidx, endidx) + else: + return [text] + + def prompt_response(self, response_obj): + resp_list = response_obj if isinstance(response_obj, list) else [response_obj] + def format_return_status(return_status): + if return_status: + return green("OK") + else: + return red("FAIL") + + for response in resp_list: + response_str = "{id:^3} - {msg} ({stat})".format(id=response.id, + msg=response.msg, + stat=format_return_status(response.success)) + print response_str + return + + def do_remove_all_streams(self, line): + '''Acquire ports\n''' + + # make sure that the user wants to acquire all + args = line.split() + if len(args) < 1: + print magenta("Please provide a list of ports separated by spaces, " + "or specify 'all' to remove from all acquired ports") + return + if args[0] == "all": + ask = ConfirmMenu('Are you sure you want to remove all stream packs from all acquired ports? ') + rc = ask.show() + if rc == False: + print yellow("[ABORTED]\n") + return + else: + port_list = self.stateless_client.get_acquired_ports() + else: + port_list = self.extract_port_ids_from_line(line) + + # rc, resp_list = self.stateless_client.take_ownership(port_list, force) + try: + res_ok, log = self.stateless_client.remove_all_streams(port_list) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + except ValueError as e: + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + + def complete_remove_all_streams(self, text, line, begidx, endidx): + return self.port_auto_complete(text, line, begidx, endidx) + + def do_start(self, line): + '''Start selected traffic in specified ports on TRex\n''' + # make sure that the user wants to acquire all + parser = parsing_opts.gen_parser("start", self.do_start.__doc__, + parsing_opts.PORT_LIST_WITH_ALL, + parsing_opts.FORCE, + parsing_opts.STREAM_FROM_PATH_OR_FILE, + parsing_opts.DURATION, + parsing_opts.MULTIPLIER) + opts = parser.parse_args(line.split()) + if opts is None: + # avoid further processing in this command + return + # print opts + port_list = self.extract_port_list(opts) + # print port_list + if opts.force: + # stop all active ports, if any + res_ok = self.stop_traffic(set(self.stateless_client.get_active_ports()).intersection(port_list)) + if not res_ok: + print yellow("[ABORTED]\n") + return + # remove all traffic from ports + res_ok = self.remove_all_streams(port_list) + if not res_ok: + print yellow("[ABORTED]\n") + return + # decide which traffic to use + stream_pack_name = None + if opts.db: + # use pre-loaded traffic + print format_text('{:<30}'.format("Load stream pack (from DB):"), 'bold'), + if opts.db not in self.streams_db.get_loaded_streams_names(): + print format_text("[FAILED]\n", 'red', 'bold') + print yellow("[ABORTED]\n") + return + else: + stream_pack_name = opts.db + else: + # try loading a YAML file + print format_text('{:<30}'.format("Load stream pack (from file):"), 'bold'), + stream_list = CStreamList() + loaded_obj = stream_list.load_yaml(opts.file[0]) + # print self.stateless_client.pretty_json(json.dumps(loaded_obj)) + try: + compiled_streams = stream_list.compile_streams() + res_ok = self.streams_db.load_streams(opts.file[1], + LoadedStreamList(loaded_obj, + [StreamPack(v.stream_id, v.stream.dump()) + for k, v in compiled_streams.items()])) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + print yellow("[ABORTED]\n") + return + print format_text("[SUCCESS]\n", 'green', 'bold') + stream_pack_name = opts.file[1] + except Exception as e: + print format_text("[FAILED]\n", 'red', 'bold') + print yellow("[ABORTED]\n") + res_ok = self.attach_to_port(stream_pack_name, port_list) + if not res_ok: + print yellow("[ABORTED]\n") + return + # finally, start the traffic + res_ok = self.start_traffic(opts.mult, port_list) + if not res_ok: + print yellow("[ABORTED]\n") + return + return + + def help_start(self): + self.do_start("-h") + + def do_stop(self, line): + '''Stop active traffic in specified ports on TRex\n''' + parser = parsing_opts.gen_parser("stop", self.do_stop.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + opts = parser.parse_args(line.split()) + if opts is None: + # avoid further processing in this command + return + port_list = self.extract_port_list(opts) + res_ok = self.stop_traffic(port_list) + return + + + def help_stop(self): + self.do_stop("-h") + + + def do_debug(self, line): + '''Enter DEBUG mode of the console to invoke smaller building blocks with server''' + i = DebugTRexConsole(self) + i.prompt = self.prompt[:-3] + ':' + blue('debug') + ' > ' + i.cmdloop() + + # aliasing + do_exit = do_EOF = do_q = do_quit + + # ----- utility methods ----- # + + def start_traffic(self, multiplier, port_list):#, silent=True): + print format_text('{:<30}'.format("Start traffic:"), 'bold'), + try: + res_ok, log = self.stateless_client.start_traffic(multiplier, port_id=port_list) + if not self._silent: + print '' + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return False + print format_text("[SUCCESS]\n", 'green', 'bold') + return True + except ValueError as e: + print '' + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + return False + + def attach_to_port(self, stream_pack_name, port_list): + print format_text('{:<30}'.format("Attaching traffic to ports:"), 'bold'), + stream_list = self.streams_db.get_stream_pack(stream_pack_name) #user_streams[args[0]] + if not stream_list: + print "Provided stream list name '{0}' doesn't exists.".format(stream_pack_name) + print format_text("[FAILED]\n", 'red', 'bold') + return + try: + res_ok, log = self.stateless_client.add_stream_pack(stream_list.compiled, port_id=port_list) + if not self._silent: + print '' + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return False + print format_text("[SUCCESS]\n", 'green', 'bold') + return True + except ValueError as e: + print '' + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + return False + + def stop_traffic(self, port_list): + print format_text('{:<30}'.format("Stop traffic:"), 'bold'), + try: + res_ok, log = self.stateless_client.stop_traffic(port_id=port_list) + if not self._silent: + print '' + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + return True + except ValueError as e: + print '' + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + + def remove_all_streams(self, port_list): + '''Remove all streams from given port_list''' + print format_text('{:<30}'.format("Remove all streams:"), 'bold'), + try: + res_ok, log = self.stateless_client.remove_all_streams(port_id=port_list) + if not self._silent: + print '' + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + return True + except ValueError as e: + print '' + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + + + + + + def extract_port_list(self, opts): + if opts.all_ports or "all" in opts.ports: + # handling all ports + port_list = self.stateless_client.get_acquired_ports() + else: + port_list = self.extract_port_ids_from_list(opts.ports) + return port_list + + def decode_multiplier(self, opts_mult): + pass + + +class DebugTRexConsole(cmd.Cmd): + + def __init__(self, trex_main_console): + cmd.Cmd.__init__(self) + self.trex_console = trex_main_console + self.stateless_client = self.trex_console.stateless_client + self.streams_db = self.trex_console.streams_db + self.register_main_console_methods() + self.do_silent("on") + pass + + # ----- super methods overriding ----- # + def completenames(self, text, *ignored): + dotext = 'do_'+text + return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)] + + def get_names(self): + result = cmd.Cmd.get_names(self) + result += self.trex_console.get_names() + return list(set(result)) + + def register_main_console_methods(self): + main_names = set(self.trex_console.get_names()).difference(set(dir(self.__class__))) + for name in main_names: + for prefix in 'do_', 'help_', 'complete_': + if name.startswith(prefix): + self.__dict__[name] = getattr(self.trex_console, name) + + # if (name[:3] == 'do_') or (name[:5] == 'help_') or (name[:9] == 'complete_'): + # chosen.append(name) + # self.__dict__[name] = getattr(self.trex_console, name) + # # setattr(self, name, classmethod(getattr(self.trex_console, name))) + + # print chosen + # self.get_names() + + # return result + + + # ----- DEBUGGING methods ----- # + # set silent on / off + def do_silent(self, line): + '''Shows or set silent mode\n''' + if line == "": + print "\nsilent mode is " + ("on\n" if self.trex_console._silent else "off\n") + + elif line == "on": + self.verbose = True + self.stateless_client.set_verbose(True) + print green("\nsilent set to on\n") + + elif line == "off": + self.verbose = False + self.stateless_client.set_verbose(False) + print green("\nsilent set to off\n") + + else: + print magenta("\nplease specify 'on' or 'off'\n") + + def do_quit(self, line): + '''Exit the debug client back to main console\n''' + self.do_silent("off") + return True + + def do_start_traffic(self, line): + '''Start pre-submitted traffic in specified ports on TRex\n''' + # make sure that the user wants to acquire all + parser = parsing_opts.gen_parser("start_traffic", self.do_start_traffic.__doc__, + parsing_opts.PORT_LIST_WITH_ALL, parsing_opts.MULTIPLIER) + opts = parser.parse_args(line.split()) + # print opts + # return + if opts is None: + # avoid further processing in this command + return + try: + port_list = self.trex_console.extract_port_list(opts) + return self.trex_console.start_traffic(opts.mult, port_list) + except Exception as e: + print e + return + + def do_stop_traffic(self, line): + '''Stop active traffic in specified ports on TRex\n''' + parser = parsing_opts.gen_parser("stop_traffic", self.do_stop_traffic.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + opts = parser.parse_args(line.split()) + # print opts + # return + if opts is None: + # avoid further processing in this command + return + try: + port_list = self.trex_console.extract_port_list(opts) + return self.trex_console.stop_traffic(port_list) + except Exception as e: + print e + return + + + def complete_stop_traffic(self, text, line, begidx, endidx): + return self.port_auto_complete(text, line, begidx, endidx, active=True) + + # return + # # return + # # if not opts.port_list: + # # print magenta("Please provide a list of ports separated by spaces, " + # # "or specify 'all' to start traffic on all acquired ports") + # # return + # + + + return + args = line.split() + if len(args) < 1: + print magenta("Please provide a list of ports separated by spaces, " + "or specify 'all' to start traffic on all acquired ports") + return + if args[0] == "all": + ask = ConfirmMenu('Are you sure you want to start traffic at all acquired ports? ') + rc = ask.show() + if rc == False: + print yellow("[ABORTED]\n") + return + else: + port_list = self.stateless_client.get_acquired_ports() + else: + port_list = self.extract_port_ids_from_line(line) + + try: + res_ok, log = self.stateless_client.start_traffic(1.0, port_id=port_list) + self.prompt_response(log) + if not res_ok: + print format_text("[FAILED]\n", 'red', 'bold') + return + print format_text("[SUCCESS]\n", 'green', 'bold') + except ValueError as e: + print magenta(str(e)) + print format_text("[FAILED]\n", 'red', 'bold') + + def complete_start_traffic(self, text, line, begidx, endidx): + # return self.port_auto_complete(text, line, begidx, endidx) + return [text] + + def help_start_traffic(self): + self.do_start_traffic("-h") + + def help_stop_traffic(self): + self.do_stop_traffic("-h") + + # def do_help(self): + + def do_rpc (self, line): + '''Launches a RPC on the server\n''' + + if line == "": + print "\nUsage: [method name] [param dict as string]\n" + print "Example: rpc test_add {'x': 12, 'y': 17}\n" + return + + sp = line.split(' ', 1) + method = sp[0] + + params = None + bad_parse = False + if len(sp) > 1: + + try: + params = ast.literal_eval(sp[1]) + if not isinstance(params, dict): + bad_parse = True + + except ValueError as e1: + bad_parse = True + except SyntaxError as e2: + bad_parse = True + + if bad_parse: + print "\nValue should be a valid dict: '{0}'".format(sp[1]) + print "\nUsage: [method name] [param dict as string]\n" + print "Example: rpc test_add {'x': 12, 'y': 17}\n" + return + + res_ok, msg = self.stateless_client.transmit(method, params) + if res_ok: + print "\nServer Response:\n\n" + pretty_json(json.dumps(msg)) + "\n" + else: + print "\n*** " + msg + "\n" + #print "Please try 'reconnect' to reconnect to server" + + + def complete_rpc (self, text, line, begidx, endidx): + return [x + for x in self.trex_console.supported_rpc + if x.startswith(text)] + + # aliasing + do_exit = do_EOF = do_q = do_quit + +# diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index e701b7db..f983d837 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -2,6 +2,7 @@ import argparse from collections import namedtuple import sys import re +import os ArgumentPack = namedtuple('ArgumentPack', ['name_or_flags', 'options']) ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options']) @@ -14,9 +15,10 @@ ALL_PORTS = 3 PORT_LIST_WITH_ALL = 4 FILE_PATH = 5 FILE_FROM_DB = 6 -STREAM_FROM_PATH_OR_FILE = 7 -DURATION = 8 -FORCE = 9 +SERVER_IP = 7 +STREAM_FROM_PATH_OR_FILE = 8 +DURATION = 9 +FORCE = 10 # list of ArgumentGroup types MUTEX = 1 @@ -61,20 +63,27 @@ def match_multiplier(val): +def is_valid_file(filename): + if not os.path.isfile(filename): + raise argparse.ArgumentTypeError("The file '%s' does not exist" % filename) + + return filename + OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'help': "Set multiplier for stream", 'dest': "mult", 'default': 1.0, 'type': match_multiplier}), + PORT_LIST: ArgumentPack(['--port'], {"nargs": '+', - # "action": "store_" 'dest':'ports', 'metavar': 'PORTS', - # 'type': int, + 'type': int, 'help': "A list of ports on which to apply the command", 'default': []}), + ALL_PORTS: ArgumentPack(['-a'], {"action": "store_true", "dest": "all_ports", @@ -88,15 +97,22 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {"action": "store_true", 'default': False, 'help': "Set if you want to stop active ports before applying new TRex run on them."}), + FILE_PATH: ArgumentPack(['-f'], - {'metavar': ('FILE', 'DB_NAME'), + {'metavar': 'FILE', 'dest': 'file', - 'nargs': 2, - 'help': "File path to YAML file that describes a stream pack. " - "Second argument is a name to store the loaded yaml file into db."}), + 'nargs': 1, + 'type': is_valid_file, + 'help': "File path to YAML file that describes a stream pack. "}), + FILE_FROM_DB: ArgumentPack(['--db'], {'metavar': 'LOADED_STREAM_PACK', 'help': "A stream pack which already loaded into console cache."}), + + SERVER_IP: ArgumentPack(['--server'], + {'metavar': 'SERVER', + 'help': "server IP"}), + # advanced options PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST, ALL_PORTS], @@ -109,33 +125,44 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], class CCmdArgParser(argparse.ArgumentParser): - def __init__(self, *args, **kwargs): + def __init__(self, stateless_client, *args, **kwargs): super(CCmdArgParser, self).__init__(*args, **kwargs) - - # def exit(self, status=0, message=None): - # try: - # return super(CCmdArgParser, self).exit(status, message) # this will trigger system exit! - # except SystemExit: - # print "Caught system exit!!" - # return -1 - # # return + self.stateless_client = stateless_client def parse_args(self, args=None, namespace=None): try: - return super(CCmdArgParser, self).parse_args(args, namespace) + opts = super(CCmdArgParser, self).parse_args(args, namespace) + if opts is None: + return None + + if opts.all_ports: + opts.ports = self.stateless_client.get_port_ids() + + for port in opts.ports: + if not self.stateless_client._is_ports_valid(port): + self.error("port id {0} is not a valid\n".format(port)) + + return opts + except SystemExit: # recover from system exit scenarios, such as "help", or bad arguments. return None +def get_flags (opt): + return OPTIONS_DB[opt].name_or_flags -def gen_parser(op_name, description, *args): - parser = CCmdArgParser(prog=op_name, conflict_handler='resolve', - # add_help=False, +def gen_parser(stateless_client, op_name, description, *args): + parser = CCmdArgParser(stateless_client, prog=op_name, conflict_handler='resolve', description=description) for param in args: try: - argument = OPTIONS_DB[param] + + if isinstance(param, int): + argument = OPTIONS_DB[param] + else: + argument = param + if isinstance(argument, ArgumentGroup): if argument.type == MUTEX: # handle as mutually exclusive group @@ -157,5 +184,6 @@ def gen_parser(op_name, description, *args): raise KeyError("The attribute '{0}' is missing as a field of the {1} option.\n".format(cause, param)) return parser + if __name__ == "__main__": pass \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index a61881a1..06ae762a 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -30,6 +30,7 @@ import tty, termios import trex_root_path from common.trex_streams import * from client.trex_stateless_client import CTRexStatelessClient +from client.trex_stateless_client import RpcResponseStatus from common.text_opts import * from client_utils.general_utils import user_input, get_current_user import parsing_opts @@ -40,43 +41,32 @@ __version__ = "1.0" LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) - -def readch(choices=[]): - - fd = sys.stdin.fileno() - old_settings = termios.tcgetattr(fd) - try: - tty.setraw(sys.stdin.fileno()) - while True: - ch = sys.stdin.read(1) - if (ord(ch) == 3) or (ord(ch) == 4): - return None - if ch in choices: - return ch - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - - return None - -class ConfirmMenu(object): - def __init__ (self, caption): - self.caption = "{cap} [confirm] : ".format(cap=caption) - - def show(self): - sys.stdout.write(self.caption) - input = user_input() - if input: - return False - else: - # user hit Enter - return True - - class CStreamsDB(object): def __init__(self): self.stream_packs = {} + def load_yaml_file (self, filename): + + stream_pack_name = filename + if stream_pack_name in self.get_loaded_streams_names(): + self.remove_stream_packs(stream_pack_name) + + stream_list = CStreamList() + loaded_obj = stream_list.load_yaml(filename) + + try: + compiled_streams = stream_list.compile_streams() + rc = self.load_streams(stream_pack_name, + LoadedStreamList(loaded_obj, + [StreamPack(v.stream_id, v.stream.dump()) + for k, v in compiled_streams.items()])) + + except Exception as e: + return None + + return self.get_stream_pack(stream_pack_name) + def load_streams(self, name, LoadedStreamList_obj): if name in self.stream_packs: return False @@ -98,84 +88,79 @@ class CStreamsDB(object): def get_loaded_streams_names(self): return self.stream_packs.keys() - def get_stream_pack(self, name): - return self.stream_packs.get(name) - - -# multi level cmd menu -class CmdMenu(object): - def __init__ (self): - self.menus = [] - - - def add_menu (self, caption, options): - menu = {} - menu['caption'] = caption - menu['options'] = options - self.menus.append(menu) - - def show (self): - cur_level = 0 - print "\n" - - selected_path = [] - for menu in self.menus: - # show all the options - print "{0}\n".format(menu['caption']) - for i, option in enumerate(menu['options']): - print "{0}. {1}".format(i + 1, option) - - #print "\nPlease select an option: " - - choices = range(0, len(menu['options'])) - choices = [ chr(x + 48) for x in choices] - - print "" - ch = readch(choices) - print "" - - if ch == None: - return None - - selected_path.append(int(ch) - 1) - - return selected_path + def stream_pack_exists (self, name): + return name in self.get_loaded_streams_names() + def get_stream_pack(self, name): + if not self.stream_pack_exists(name): + return None + else: + return self.stream_packs.get(name) -class AddStreamMenu(CmdMenu): - def __init__ (self): - super(AddStreamMenu, self).__init__() - self.add_menu('Please select type of stream', ['a', 'b', 'c']) - self.add_menu('Please select ISG', ['d', 'e', 'f']) +# # main console object class TRexConsole(cmd.Cmd): """Trex Console""" - def __init__(self, stateless_client, verbose): + def __init__(self, stateless_client, acquire_all_ports = True, verbose = False): cmd.Cmd.__init__(self) self.stateless_client = stateless_client + self.verbose = verbose + self.acquire_all_ports = acquire_all_ports + self.do_connect("") self.intro = "\n-=TRex Console v{ver}=-\n".format(ver=__version__) self.intro += "\nType 'help' or '?' for supported actions\n" - self.verbose = False - self._silent = True - self.postcmd(False, "") - self.user_streams = {} self.streams_db = CStreamsDB() + ################### internal section ######################## + # a cool hack - i stole this function and added space def completenames(self, text, *ignored): dotext = 'do_'+text return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)] + + def register_main_console_methods(self): + main_names = set(self.trex_console.get_names()).difference(set(dir(self.__class__))) + for name in main_names: + for prefix in 'do_', 'help_', 'complete_': + if name.startswith(prefix): + self.__dict__[name] = getattr(self.trex_console, name) + + def postcmd(self, stop, line): + if self.stateless_client.is_connected(): + self.prompt = "TRex > " + else: + self.supported_rpc = None + self.prompt = "TRex (offline) > " + + return stop + + def default(self, line): + print "'{0}' is an unrecognized command. type 'help' or '?' for a list\n".format(line) + + @staticmethod + def tree_autocomplete(text): + dir = os.path.dirname(text) + if dir: + path = dir + else: + path = "." + start_string = os.path.basename(text) + return [x + for x in os.listdir(path) + if x.startswith(start_string)] + + ####################### shell commands ####################### # set verbose on / off def do_verbose(self, line): @@ -196,169 +181,11 @@ class TRexConsole(cmd.Cmd): else: print magenta("\nplease specify 'on' or 'off'\n") - # query the server for registered commands - def do_query_server(self, line): - '''query the RPC server for supported remote commands\n''' - - res_ok, msg = self.stateless_client.get_supported_cmds() - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print "\nRPC server supports the following commands:\n" - for func in msg: - if func: - print func - print '' - print format_text("[SUCCESS]\n", 'green', 'bold') - return - - def do_ping(self, line): - '''Pings the RPC server\n''' - - print "\n-> Pinging RPC server" - - res_ok, msg = self.stateless_client.ping() - if res_ok: - print format_text("[SUCCESS]\n", 'green', 'bold') - else: - print "\n*** " + msg + "\n" - return - - def do_force_acquire(self, line): - '''Acquires ports by force\n''' - - self.do_acquire(line, True) - - def complete_force_acquire(self, text, line, begidx, endidx): - return self.port_auto_complete(text, line, begidx, endidx, acquired=False) - - def extract_port_ids_from_line(self, line): - return {int(x) for x in line.split()} - - def extract_port_ids_from_list(self, port_list): - return {int(x) for x in port_list} - - def parse_ports_from_line (self, line): - port_list = set() - if line: - for port_id in line.split(' '): - if (not port_id.isdigit()) or (int(port_id) < 0) or (int(port_id) >= self.stateless_client.get_port_count()): - print "Please provide a list of ports separated by spaces between 0 and {0}".format(self.stateless_client.get_port_count() - 1) - return None - - port_list.add(int(port_id)) - - port_list = list(port_list) - - else: - port_list = [i for i in xrange(0, self.stateless_client.get_port_count())] - - return port_list - - - def do_acquire(self, line, force=False): - '''Acquire ports\n''' - - # make sure that the user wants to acquire all - args = line.split() - if len(args) < 1: - print magenta("Please provide a list of ports separated by spaces, or specify 'all' to acquire all available ports") - return - - if args[0] == "all": - ask = ConfirmMenu('Are you sure you want to acquire all ports ? ') - rc = ask.show() - if rc == False: - print yellow("[ABORTED]\n") - return - else: - port_list = self.stateless_client.get_port_ids() - else: - port_list = self.extract_port_ids_from_line(line) - - # rc, resp_list = self.stateless_client.take_ownership(port_list, force) - try: - res_ok, log = self.stateless_client.acquire(port_list, force) - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - except ValueError as e: - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - - - def port_auto_complete(self, text, line, begidx, endidx, acquired=True, active=False): - if acquired: - if not active: - ret_list = [x - for x in map(str, self.stateless_client.get_acquired_ports()) - if x.startswith(text)] - else: - ret_list = [x - for x in map(str, self.stateless_client.get_active_ports()) - if x.startswith(text)] - else: - ret_list = [x - for x in map(str, self.stateless_client.get_port_ids()) - if x.startswith(text)] - ret_list.append("all") - return ret_list - - - def complete_acquire(self, text, line, begidx, endidx): - return self.port_auto_complete(text, line, begidx, endidx, acquired=False) - - def do_release (self, line): - '''Release ports\n''' - - # if line: - # port_list = self.parse_ports_from_line(line) - # else: - # port_list = self.stateless_client.get_owned_ports() - args = line.split() - if len(args) < 1: - print "Please provide a list of ports separated by spaces, or specify 'all' to acquire all available ports" - if args[0] == "all": - ask = ConfirmMenu('Are you sure you want to release all acquired ports? ') - rc = ask.show() - if rc == False: - print yellow("[ABORTED]\n") - return - else: - port_list = self.stateless_client.get_acquired_ports() - else: - port_list = self.extract_port_ids_from_line(line) - - try: - res_ok, log = self.stateless_client.release(port_list) - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - except ValueError as e: - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - return - - def complete_release(self, text, line, begidx, endidx): - return self.port_auto_complete(text, line, begidx, endidx) - + ############### connect def do_connect (self, line): '''Connects to the server\n''' - if line == "": - res_ok, msg = self.stateless_client.connect() - else: - sp = line.split() - if (len(sp) != 2): - print "\n[usage] connect [server] [port] or without parameters\n" - return - - res_ok, msg = self.stateless_client.connect(sp[0], sp[1]) - + res_ok, msg = self.stateless_client.connect() if res_ok: print format_text("[SUCCESS]\n", 'green', 'bold') else: @@ -368,730 +195,175 @@ class TRexConsole(cmd.Cmd): self.supported_rpc = self.stateless_client.get_supported_cmds().data - # def do_rpc (self, line): - # '''Launches a RPC on the server\n''' - # - # if line == "": - # print "\nUsage: [method name] [param dict as string]\n" - # print "Example: rpc test_add {'x': 12, 'y': 17}\n" - # return - # - # sp = line.split(' ', 1) - # method = sp[0] - # - # params = None - # bad_parse = False - # if len(sp) > 1: - # - # try: - # params = ast.literal_eval(sp[1]) - # if not isinstance(params, dict): - # bad_parse = True - # - # except ValueError as e1: - # bad_parse = True - # except SyntaxError as e2: - # bad_parse = True - # - # if bad_parse: - # print "\nValue should be a valid dict: '{0}'".format(sp[1]) - # print "\nUsage: [method name] [param dict as string]\n" - # print "Example: rpc test_add {'x': 12, 'y': 17}\n" - # return - # - # res_ok, msg = self.stateless_client.transmit(method, params) - # if res_ok: - # print "\nServer Response:\n\n" + pretty_json(json.dumps(msg)) + "\n" - # else: - # print "\n*** " + msg + "\n" - # #print "Please try 'reconnect' to reconnect to server" - # - # - # def complete_rpc (self, text, line, begidx, endidx): - # return [x - # for x in self.supported_rpc - # if x.startswith(text)] - - def do_status (self, line): - '''Shows a graphical console\n''' - - if not self.stateless_client.is_connected(): - print "Not connected to server\n" - return - - self.do_verbose('off') - trex_status.show_trex_status(self.stateless_client) - - def do_quit(self, line): - '''Exit the client\n''' - return True + if self.acquire_all_ports: + res_ok, log = self.stateless_client.acquire(self.stateless_client.get_port_ids()) + if not res_ok: + print "\n*** Failed to acquire all ports... exiting...""" - def do_disconnect (self, line): - '''Disconnect from the server\n''' - if not self.stateless_client.is_connected(): - print "Not connected to server\n" + @staticmethod + def annotate (desc, rc = None, err_log = None, ext_err_msg = None): + print format_text('\n{:<40}'.format(desc), 'bold'), + if rc == None: + print "\n" return - res_ok, msg = self.stateless_client.disconnect() - if res_ok: - print format_text("[SUCCESS]\n", 'green', 'bold') - else: - print msg + "\n" - - def do_whoami (self, line): - '''Prints console user name\n''' - print "\n" + self.stateless_client.user + "\n" - - def postcmd(self, stop, line): - if self.stateless_client.is_connected(): - self.prompt = "TRex > " - else: - self.supported_rpc = None - self.prompt = "TRex (offline) > " - - return stop - - def default(self, line): - print "'{0}' is an unrecognized command. type 'help' or '?' for a list\n".format(line) + if rc == False: + # do we have a complex log object ? + if isinstance(err_log, list): + print "" + for func in err_log: + if func: + print func + print "" - # def do_help (self, line): - # '''Shows This Help Screen\n''' - # if line: - # try: - # func = getattr(self, 'help_' + line) - # except AttributeError: - # try: - # doc = getattr(self, 'do_' + line).__doc__ - # if doc: - # self.stdout.write("%s\n"%str(doc)) - # return - # except AttributeError: - # pass - # self.stdout.write("%s\n"%str(self.nohelp % (line,))) - # return - # func() - # return - # - # print "\nSupported Console Commands:" - # print "----------------------------\n" - # - # cmds = [x[3:] for x in self.get_names() if x.startswith("do_")] - # for cmd in cmds: - # if cmd == "EOF": - # continue - # - # try: - # doc = getattr(self, 'do_' + cmd).__doc__ - # if doc: - # help = str(doc) - # else: - # help = "*** Undocumented Function ***\n" - # except AttributeError: - # help = "*** Undocumented Function ***\n" - # - # print "{:<30} {:<30}".format(cmd + " - ", help) - - def do_stream_db_add(self, line): - '''Loads a YAML stream list serialization into user console \n''' - args = line.split() - if len(args) >= 2: - name = args[0] - yaml_path = args[1] - try: - multiplier = args[2] - except IndexError: - multiplier = 1 - stream_list = CStreamList() - loaded_obj = stream_list.load_yaml(yaml_path, multiplier) - # print self.stateless_client.pretty_json(json.dumps(loaded_obj)) - try: - compiled_streams = stream_list.compile_streams() - res_ok = self.streams_db.load_streams(name, LoadedStreamList(loaded_obj, - [StreamPack(v.stream_id, v.stream.dump()) - for k, v in compiled_streams.items()])) - if res_ok: - print green("Stream pack '{0}' loaded and added successfully\n".format(name)) - else: - print magenta("Picked name already exist. Please pick another name.\n") - except Exception as e: - print "adding new stream failed due to the following error:\n", str(e) - print format_text("[FAILED]\n", 'red', 'bold') - - return - else: - print magenta("please provide load name and YAML path, separated by space.\n" - "Optionally, you may provide a third argument to specify multiplier.\n") - - @staticmethod - def tree_autocomplete(text): - dir = os.path.dirname(text) - if dir: - path = dir - else: - path = "." - start_string = os.path.basename(text) - return [x - for x in os.listdir(path) - if x.startswith(start_string)] + elif isinstance(err_log, str): + print "\n" + err_log + "\n" + print format_text("[FAILED]\n", 'red', 'bold') + if ext_err_msg: + print format_text(ext_err_msg + "\n", 'blue', 'bold') - def complete_stream_db_add(self, text, line, begidx, endidx): - arg_num = len(line.split()) - 1 - if arg_num == 2: - return TRexConsole.tree_autocomplete(line.split()[-1]) - else: - return [text] - - def do_stream_db_show(self, line): - '''Shows the loaded stream list named [name] \n''' - args = line.split() - if args: - list_name = args[0] - try: - stream = self.streams_db.get_stream_pack(list_name)#user_streams[list_name] - if len(args) >= 2 and args[1] == "full": - print pretty_json(json.dumps(stream.compiled)) - else: - print pretty_json(json.dumps(stream.loaded)) - except KeyError as e: - print "Unknown stream list name provided" - else: - print "Available stream packs:\n{0}".format(', '.join(sorted(self.streams_db.get_loaded_streams_names()))) + return False - def complete_stream_db_show(self, text, line, begidx, endidx): - return [x - for x in self.streams_db.get_loaded_streams_names() - if x.startswith(text)] - - def do_stream_db_remove(self, line): - '''Removes a single loaded stream packs from loaded stream pack repository\n''' - args = line.split() - if args: - removed_streams = self.streams_db.remove_stream_packs(*args) - if removed_streams: - print green("The following stream packs were removed:") - print bold(", ".join(sorted(removed_streams))) - print format_text("[SUCCESS]\n", 'green', 'bold') - else: - print red("No streams were removed. Make sure to provide valid stream pack names.") else: - print magenta("Please provide stream pack name(s), separated with spaces.") - - def do_stream_db_clear(self, line): - '''Clears all loaded stream packs from loaded stream pack repository\n''' - self.streams_db.clear() - print format_text("[SUCCESS]\n", 'green', 'bold') + print format_text("[SUCCESS]\n", 'green', 'bold') + return True - def complete_stream_db_remove(self, text, line, begidx, endidx): - return [x - for x in self.streams_db.get_loaded_streams_names() - if x.startswith(text)] - - - def do_attach(self, line): - '''Assign loaded stream pack into specified ports on TRex\n''' - args = line.split() - if len(args) >= 2: - stream_pack_name = args[0] - stream_list = self.streams_db.get_stream_pack(stream_pack_name) #user_streams[args[0]] - if not stream_list: - print "Provided stream list name '{0}' doesn't exists.".format(stream_pack_name) - print format_text("[FAILED]\n", 'red', 'bold') - return - if args[1] == "all": - ask = ConfirmMenu('Are you sure you want to release all acquired ports? ') - rc = ask.show() - if rc == False: - print yellow("[ABORTED]\n") - return - else: - port_list = self.stateless_client.get_acquired_ports() - else: - port_list = self.extract_port_ids_from_line(' '.join(args[1:])) - owned = set(self.stateless_client.get_acquired_ports()) - try: - if set(port_list).issubset(owned): - res_ok, log = self.stateless_client.add_stream_pack(stream_list.compiled, port_id=port_list) - # res_ok, msg = self.stateless_client.add_stream(port_list, stream_list.compiled) - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - return - else: - print "Not all desired ports are acquired.\n" \ - "Acquired ports are: {acq}\n" \ - "Requested ports: {req}\n" \ - "Missing ports: {miss}".format(acq=list(owned), - req=port_list, - miss=list(set(port_list).difference(owned))) - print format_text("[FAILED]\n", 'red', 'bold') - except ValueError as e: - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - else: - print magenta("Please provide list name and ports to attach to, " - "or specify 'all' to attach all owned ports.\n") - - def complete_attach(self, text, line, begidx, endidx): - arg_num = len(line.split()) - 1 - if arg_num == 1: - # return optional streams packs - if line.endswith(" "): - return self.port_auto_complete(text, line, begidx, endidx) - return [x - for x in self.streams_db.get_loaded_streams_names() - if x.startswith(text)] - elif arg_num >= 2: - # return optional ports to attach to - return self.port_auto_complete(text, line, begidx, endidx) - else: - return [text] - - def prompt_response(self, response_obj): - resp_list = response_obj if isinstance(response_obj, list) else [response_obj] - def format_return_status(return_status): - if return_status: - return green("OK") - else: - return red("FAIL") - - for response in resp_list: - response_str = "{id:^3} - {msg} ({stat})".format(id=response.id, - msg=response.msg, - stat=format_return_status(response.success)) - print response_str - return + ############### start - def do_remove_all_streams(self, line): - '''Acquire ports\n''' + def complete_start(self, text, line, begidx, endidx): + s = line.split() + l = len(s) - # make sure that the user wants to acquire all - args = line.split() - if len(args) < 1: - print magenta("Please provide a list of ports separated by spaces, " - "or specify 'all' to remove from all acquired ports") - return - if args[0] == "all": - ask = ConfirmMenu('Are you sure you want to remove all stream packs from all acquired ports? ') - rc = ask.show() - if rc == False: - print yellow("[ABORTED]\n") - return - else: - port_list = self.stateless_client.get_acquired_ports() - else: - port_list = self.extract_port_ids_from_line(line) + file_flags = parsing_opts.get_flags(parsing_opts.FILE_PATH) - # rc, resp_list = self.stateless_client.take_ownership(port_list, force) - try: - res_ok, log = self.stateless_client.remove_all_streams(port_list) - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - except ValueError as e: - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') + if (l > 1) and (s[l - 1] in file_flags): + return TRexConsole.tree_autocomplete("") - def complete_remove_all_streams(self, text, line, begidx, endidx): - return self.port_auto_complete(text, line, begidx, endidx) + if (l > 2) and (s[l - 2] in file_flags): + return TRexConsole.tree_autocomplete(s[l - 1]) def do_start(self, line): '''Start selected traffic in specified ports on TRex\n''' + # make sure that the user wants to acquire all - parser = parsing_opts.gen_parser("start", self.do_start.__doc__, + parser = parsing_opts.gen_parser(self.stateless_client, + "start", + self.do_start.__doc__, parsing_opts.PORT_LIST_WITH_ALL, parsing_opts.FORCE, parsing_opts.STREAM_FROM_PATH_OR_FILE, parsing_opts.DURATION, parsing_opts.MULTIPLIER) + opts = parser.parse_args(line.split()) + if opts is None: - # avoid further processing in this command - return - # print opts - port_list = self.extract_port_list(opts) - # print port_list - if opts.force: - # stop all active ports, if any - res_ok = self.stop_traffic(set(self.stateless_client.get_active_ports()).intersection(port_list)) - if not res_ok: - print yellow("[ABORTED]\n") - return - # remove all traffic from ports - res_ok = self.remove_all_streams(port_list) - if not res_ok: - print yellow("[ABORTED]\n") return - # decide which traffic to use - stream_pack_name = None + if opts.db: - # use pre-loaded traffic - print format_text('{:<30}'.format("Load stream pack (from DB):"), 'bold'), - if opts.db not in self.streams_db.get_loaded_streams_names(): - print format_text("[FAILED]\n", 'red', 'bold') - print yellow("[ABORTED]\n") + stream_list = self.stream_db.get_stream_pack(opts.db) + self.annotate("Load stream pack (from DB):", (stream_list != None)) + if stream_list == None: return - else: - stream_pack_name = opts.db + else: - # try loading a YAML file - print format_text('{:<30}'.format("Load stream pack (from file):"), 'bold'), - stream_list = CStreamList() - loaded_obj = stream_list.load_yaml(opts.file[0]) - # print self.stateless_client.pretty_json(json.dumps(loaded_obj)) - try: - compiled_streams = stream_list.compile_streams() - res_ok = self.streams_db.load_streams(opts.file[1], - LoadedStreamList(loaded_obj, - [StreamPack(v.stream_id, v.stream.dump()) - for k, v in compiled_streams.items()])) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - print yellow("[ABORTED]\n") - return - print format_text("[SUCCESS]\n", 'green', 'bold') - stream_pack_name = opts.file[1] - except Exception as e: - print format_text("[FAILED]\n", 'red', 'bold') - print yellow("[ABORTED]\n") - res_ok = self.attach_to_port(stream_pack_name, port_list) - if not res_ok: - print yellow("[ABORTED]\n") - return - # finally, start the traffic - res_ok = self.start_traffic(opts.mult, port_list) - if not res_ok: - print yellow("[ABORTED]\n") - return + # load streams from file + stream_list = self.streams_db.load_yaml_file(opts.file[0]) + self.annotate("Load stream pack (from file):", (stream_list != None)) + if stream_list == None: + return + + + self.stateless_client.cmd_start(opts.ports, stream_list, opts.mult, opts.force, self.annotate) return + def help_start(self): self.do_start("-h") + ############# stop def do_stop(self, line): '''Stop active traffic in specified ports on TRex\n''' - parser = parsing_opts.gen_parser("stop", self.do_stop.__doc__, + parser = parsing_opts.gen_parser(self.stateless_client, + "stop", + self.do_stop.__doc__, parsing_opts.PORT_LIST_WITH_ALL) + opts = parser.parse_args(line.split()) if opts is None: - # avoid further processing in this command return - port_list = self.extract_port_list(opts) - res_ok = self.stop_traffic(port_list) - return + self.stateless_client.cmd_stop(opts.ports, self.annotate) + return def help_stop(self): self.do_stop("-h") + ########## reset + def do_reset (self, line): + '''force stop all ports\n''' + self.stateless_client.cmd_reset(self.annotate) - def do_debug(self, line): - '''Enter DEBUG mode of the console to invoke smaller building blocks with server''' - i = DebugTRexConsole(self) - i.prompt = self.prompt[:-3] + ':' + blue('debug') + ' > ' - i.cmdloop() - - # aliasing - do_exit = do_EOF = do_q = do_quit - - # ----- utility methods ----- # - - def start_traffic(self, multiplier, port_list):#, silent=True): - print format_text('{:<30}'.format("Start traffic:"), 'bold'), - try: - res_ok, log = self.stateless_client.start_traffic(multiplier, port_id=port_list) - if not self._silent: - print '' - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return False - print format_text("[SUCCESS]\n", 'green', 'bold') - return True - except ValueError as e: - print '' - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - return False + + # tui + def do_tui (self, line): + '''Shows a graphical console\n''' - def attach_to_port(self, stream_pack_name, port_list): - print format_text('{:<30}'.format("Attaching traffic to ports:"), 'bold'), - stream_list = self.streams_db.get_stream_pack(stream_pack_name) #user_streams[args[0]] - if not stream_list: - print "Provided stream list name '{0}' doesn't exists.".format(stream_pack_name) - print format_text("[FAILED]\n", 'red', 'bold') + if not self.stateless_client.is_connected(): + print "Not connected to server\n" return - try: - res_ok, log = self.stateless_client.add_stream_pack(stream_list.compiled, port_id=port_list) - if not self._silent: - print '' - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return False - print format_text("[SUCCESS]\n", 'green', 'bold') - return True - except ValueError as e: - print '' - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - return False - - def stop_traffic(self, port_list): - print format_text('{:<30}'.format("Stop traffic:"), 'bold'), - try: - res_ok, log = self.stateless_client.stop_traffic(port_id=port_list) - if not self._silent: - print '' - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - return True - except ValueError as e: - print '' - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - - def remove_all_streams(self, port_list): - '''Remove all streams from given port_list''' - print format_text('{:<30}'.format("Remove all streams:"), 'bold'), - try: - res_ok, log = self.stateless_client.remove_all_streams(port_id=port_list) - if not self._silent: - print '' - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - return True - except ValueError as e: - print '' - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - - - - - - def extract_port_list(self, opts): - if opts.all_ports or "all" in opts.ports: - # handling all ports - port_list = self.stateless_client.get_acquired_ports() - else: - port_list = self.extract_port_ids_from_list(opts.ports) - return port_list - - def decode_multiplier(self, opts_mult): - pass - - -class DebugTRexConsole(cmd.Cmd): - - def __init__(self, trex_main_console): - cmd.Cmd.__init__(self) - self.trex_console = trex_main_console - self.stateless_client = self.trex_console.stateless_client - self.streams_db = self.trex_console.streams_db - self.register_main_console_methods() - self.do_silent("on") - pass - - # ----- super methods overriding ----- # - def completenames(self, text, *ignored): - dotext = 'do_'+text - return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)] - def get_names(self): - result = cmd.Cmd.get_names(self) - result += self.trex_console.get_names() - return list(set(result)) - - def register_main_console_methods(self): - main_names = set(self.trex_console.get_names()).difference(set(dir(self.__class__))) - for name in main_names: - for prefix in 'do_', 'help_', 'complete_': - if name.startswith(prefix): - self.__dict__[name] = getattr(self.trex_console, name) - - # if (name[:3] == 'do_') or (name[:5] == 'help_') or (name[:9] == 'complete_'): - # chosen.append(name) - # self.__dict__[name] = getattr(self.trex_console, name) - # # setattr(self, name, classmethod(getattr(self.trex_console, name))) - - # print chosen - # self.get_names() - - # return result - - - # ----- DEBUGGING methods ----- # - # set silent on / off - def do_silent(self, line): - '''Shows or set silent mode\n''' - if line == "": - print "\nsilent mode is " + ("on\n" if self.trex_console._silent else "off\n") - - elif line == "on": - self.verbose = True - self.stateless_client.set_verbose(True) - print green("\nsilent set to on\n") - - elif line == "off": - self.verbose = False - self.stateless_client.set_verbose(False) - print green("\nsilent set to off\n") - - else: - print magenta("\nplease specify 'on' or 'off'\n") + self.do_verbose('off') + trex_status.show_trex_status(self.stateless_client) + # quit function def do_quit(self, line): - '''Exit the debug client back to main console\n''' - self.do_silent("off") + '''Exit the client\n''' return True - def do_start_traffic(self, line): - '''Start pre-submitted traffic in specified ports on TRex\n''' - # make sure that the user wants to acquire all - parser = parsing_opts.gen_parser("start_traffic", self.do_start_traffic.__doc__, - parsing_opts.PORT_LIST_WITH_ALL, parsing_opts.MULTIPLIER) - opts = parser.parse_args(line.split()) - # print opts - # return - if opts is None: - # avoid further processing in this command - return - try: - port_list = self.trex_console.extract_port_list(opts) - return self.trex_console.start_traffic(opts.mult, port_list) - except Exception as e: - print e - return - - def do_stop_traffic(self, line): - '''Stop active traffic in specified ports on TRex\n''' - parser = parsing_opts.gen_parser("stop_traffic", self.do_stop_traffic.__doc__, - parsing_opts.PORT_LIST_WITH_ALL) - opts = parser.parse_args(line.split()) - # print opts - # return - if opts is None: - # avoid further processing in this command - return - try: - port_list = self.trex_console.extract_port_list(opts) - return self.trex_console.stop_traffic(port_list) - except Exception as e: - print e - return - - - def complete_stop_traffic(self, text, line, begidx, endidx): - return self.port_auto_complete(text, line, begidx, endidx, active=True) - - # return - # # return - # # if not opts.port_list: - # # print magenta("Please provide a list of ports separated by spaces, " - # # "or specify 'all' to start traffic on all acquired ports") - # # return - # - - - return - args = line.split() - if len(args) < 1: - print magenta("Please provide a list of ports separated by spaces, " - "or specify 'all' to start traffic on all acquired ports") - return - if args[0] == "all": - ask = ConfirmMenu('Are you sure you want to start traffic at all acquired ports? ') - rc = ask.show() - if rc == False: - print yellow("[ABORTED]\n") - return - else: - port_list = self.stateless_client.get_acquired_ports() - else: - port_list = self.extract_port_ids_from_line(line) - - try: - res_ok, log = self.stateless_client.start_traffic(1.0, port_id=port_list) - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - except ValueError as e: - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - - def complete_start_traffic(self, text, line, begidx, endidx): - # return self.port_auto_complete(text, line, begidx, endidx) - return [text] - - def help_start_traffic(self): - self.do_start_traffic("-h") - - def help_stop_traffic(self): - self.do_stop_traffic("-h") - - # def do_help(self): - - def do_rpc (self, line): - '''Launches a RPC on the server\n''' + + def do_help (self, line): + '''Shows This Help Screen\n''' + if line: + try: + func = getattr(self, 'help_' + line) + except AttributeError: + try: + doc = getattr(self, 'do_' + line).__doc__ + if doc: + self.stdout.write("%s\n"%str(doc)) + return + except AttributeError: + pass + self.stdout.write("%s\n"%str(self.nohelp % (line,))) + return + func() + return + + print "\nSupported Console Commands:" + print "----------------------------\n" + + cmds = [x[3:] for x in self.get_names() if x.startswith("do_")] + for cmd in cmds: + if ( (cmd == "EOF") or (cmd == "q") or (cmd == "exit")): + continue + + try: + doc = getattr(self, 'do_' + cmd).__doc__ + if doc: + help = str(doc) + else: + help = "*** Undocumented Function ***\n" + except AttributeError: + help = "*** Undocumented Function ***\n" + + print "{:<30} {:<30}".format(cmd + " - ", help) - if line == "": - print "\nUsage: [method name] [param dict as string]\n" - print "Example: rpc test_add {'x': 12, 'y': 17}\n" - return - - sp = line.split(' ', 1) - method = sp[0] - - params = None - bad_parse = False - if len(sp) > 1: - - try: - params = ast.literal_eval(sp[1]) - if not isinstance(params, dict): - bad_parse = True - - except ValueError as e1: - bad_parse = True - except SyntaxError as e2: - bad_parse = True - - if bad_parse: - print "\nValue should be a valid dict: '{0}'".format(sp[1]) - print "\nUsage: [method name] [param dict as string]\n" - print "Example: rpc test_add {'x': 12, 'y': 17}\n" - return - - res_ok, msg = self.stateless_client.transmit(method, params) - if res_ok: - print "\nServer Response:\n\n" + pretty_json(json.dumps(msg)) + "\n" - else: - print "\n*** " + msg + "\n" - #print "Please try 'reconnect' to reconnect to server" - - - def complete_rpc (self, text, line, begidx, endidx): - return [x - for x in self.trex_console.supported_rpc - if x.startswith(text)] - - # aliasing do_exit = do_EOF = do_q = do_quit + def setParserOptions(): parser = argparse.ArgumentParser(prog="trex_console.py") @@ -1099,12 +371,12 @@ def setParserOptions(): default = "localhost", type = str) - parser.add_argument("-p", "--port", help = "TRex Server Port [default is 4505]\n", - default = 4505, + parser.add_argument("-p", "--port", help = "TRex Server Port [default is 5505]\n", + default = 5505, type = int) - parser.add_argument("--async_port", help = "TRex ASync Publisher Port [default is 4506]\n", - default = 4506, + parser.add_argument("--async_port", help = "TRex ASync Publisher Port [default is 4505]\n", + default = 4505, dest='pub', type = int) @@ -1116,6 +388,11 @@ def setParserOptions(): action="store_true", help="Switch ON verbose option. Default is: OFF.", default = False) + + parser.add_argument("--no_acquire", dest="acquire", + action="store_false", help="Acquire all ports on connect. Default is: ON.", + default = True) + return parser @@ -1128,7 +405,7 @@ def main(): # console try: - console = TRexConsole(stateless_client, options.verbose) + console = TRexConsole(stateless_client, options.acquire, options.verbose) console.cmdloop() except KeyboardInterrupt as e: print "\n\n*** Caught Ctrl + C... Exiting...\n\n" diff --git a/scripts/automation/trex_control_plane/console/trex_status.py b/scripts/automation/trex_control_plane/console/trex_status.py index 2b97d7d3..a54b718e 100644 --- a/scripts/automation/trex_control_plane/console/trex_status.py +++ b/scripts/automation/trex_control_plane/console/trex_status.py @@ -168,7 +168,8 @@ class PortsStatsPanel(TrexStatusPanel): port_stats.get_rel("ibytes", format = True, suffix = "B")))) else: - self.getwin().addstr(5 + (i * 4), 2, 2, "{:^15} {:^30} {:^30} {:^30}".format( + + self.getwin().addstr(5 + (i * 4), 2, "{:^15} {:^30} {:^30} {:^30}".format( "{0} ({1})".format(str(port_index), self.status_obj.server_sys_info["ports"][port_index]["speed"]), "N/A", "N/A", @@ -284,7 +285,7 @@ class SinglePortPanel(TrexStatusPanel): port_stats.get_rel("ibytes", format = True, suffix = "B")))) else: - self.getwin().addstr(y + (i * 4), 2, 2, "{:^15} {:^30} {:^30} {:^30}".format( + self.getwin().addstr(y, 2, "{:^15} {:^30} {:^30} {:^30}".format( "{0} ({1})".format(str(self.port_id), self.status_obj.server_sys_info["ports"][self.port_id]["speed"]), "N/A", "N/A", -- cgit From 45b71cff9d0465b77f82e4cd40b64a9f3183c1c7 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Thu, 12 Nov 2015 15:33:30 +0200 Subject: refactor stream object --- scripts/automation/trex_control_plane/client/trex_async_client.py | 3 ++- scripts/automation/trex_control_plane/console/trex_console.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index 72cce5aa..d13513bf 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -181,7 +181,8 @@ class CTRexAsyncClient(): self.socket.setsockopt(zmq.SUBSCRIBE, '') while self.active: - msg = json.loads(self.socket.recv_string()) + line = self.socket.recv_string(); + msg = json.loads(line) key = msg['name'] self.raw_snapshot[key] = msg['data'] diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 06ae762a..5470e694 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -371,12 +371,12 @@ def setParserOptions(): default = "localhost", type = str) - parser.add_argument("-p", "--port", help = "TRex Server Port [default is 5505]\n", - default = 5505, + parser.add_argument("-p", "--port", help = "TRex Server Port [default is 4501]\n", + default = 4501, type = int) - parser.add_argument("--async_port", help = "TRex ASync Publisher Port [default is 4505]\n", - default = 4505, + parser.add_argument("--async_port", help = "TRex ASync Publisher Port [default is 4500]\n", + default = 4500, dest='pub', type = int) -- cgit From 78c6593c5a2d3d2242be7fc659d15eac6b869358 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 12 Nov 2015 16:33:05 +0200 Subject: DRAFT - only for internal purpose --- .../client/trex_stateless_client.py | 804 ++++++++++++--------- .../client_utils/jsonrpc_client.py | 273 +------ .../trex_control_plane/console/trex_console.py | 96 ++- 3 files changed, 515 insertions(+), 658 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 168853b3..5a7b1873 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -24,148 +24,331 @@ class RpcResponseStatus(namedtuple('RpcResponseStatus', ['success', 'id', 'msg'] msg=self.msg, stat="success" if self.success else "fail") -# RpcResponseStatus = namedtuple('RpcResponseStatus', ['success', 'id', 'msg']) +# simple class to represent complex return value +class RC: + + def __init__ (self, rc, data): + self.rc = rc + self.data = data + + def good (self): + return self.rc + + def bad (self): + return not self.rc + + def data (self): + if self.good(): + return self.data + else: + return "" + + def err (self): + if self.bad(): + return self.data + else: + return "" + +RC_OK = RC(True, "") +def RC_ERR (err): + return RC(False, err) + +class RC_LIST: + def __init__ (self): + self.rc_list = [] + + def add (self, rc): + self.rc_list.append(rc) + + def good(self): + return all([x.good() for x in self.rc_list]) + + def bad (self): + not self.good() + + def data (self): + return [x.data() for x in self.rc_list] + + def err (self): + return [x.err() for x in self.rc_list] + + +# describes a single port +class Port: + + STATE_DOWN = 0 + STATE_IDLE = 1 + STATE_STREAMS = 2 + STATE_TX = 3 + STATE_PAUSE = 4 + + def __init__ (self, port_id, user, transmit): + self.port_id = port_id + self.state = self.STATE_IDLE + self.handler = None + self.transmit = transmit + self.user = user + + self.streams = {} + + def err (self, msg): + return RC_ERR("port {0} : {1}".format(self.port_id, msg)) + + # take the port + def acquire (self, force = False): + params = {"port_id": self.port_id, + "user": self.user, + "force": force} + + command = RpcCmdData("acquire", params) + rc = self.transmit(command.method, command.params) + if rc.success: + self.handler = rc.data + return RC_OK + else: + return RC_ERR(rc.data) + + + # release the port + def release (self): + params = {"port_id": self.port_id, + "handler": self.handler} + + command = RpcCmdData("release", params) + rc = self.transmit(command.method, command.params) + if rc.success: + self.handler = rc.data + return RC_OK + else: + return RC_ERR(rc.data) + + def is_acquired (self): + return (self.handler != None) + + def is_active (self): + return (self.state == self.STATE_TX ) or (self.state == self.STATE_PAUSE) + + def sync (self, sync_data): + + self.handler = sync_data['handler'] + + if sync_data['state'] == "DOWN": + self.state = self.STATE_DOWN + elif sync_data['state'] == "IDLE": + self.state = self.STATE_IDLE + elif sync_data['state'] == "STREAMS": + self.state = self.STATE_STREAMS + elif sync_data['state'] == "TX": + self.state = self.STATE_TX + elif sync_data['state'] == "PAUSE": + self.state = self.STATE_PAUSE + else: + raise Exception("port {0}: bad state received from server '{1}'".format(self.port_id, sync_data['state'])) + + return RC_OK + + + # return TRUE if write commands + def is_port_writeable (self): + # operations on port can be done on state idle or state sreams + return ((self.state == STATE_IDLE) or (self.state == STATE_STREAMS)) + + # add stream to the port + def add_stream (self, stream_id, stream_obj): + + if not self.is_port_writeable(): + return self.err("Please stop port before attempting to add streams") + + + params = {"handler": self.handler, + "port_id": self.port_id, + "stream_id": stream_id, + "stream": stream_obj.dump()} + + rc, data = self.transmit("add_stream", params) + if not rc: + return self.err(data) + + # add the stream + self.streams[stream_id] = stream_obj + + # the only valid state now + self.state = self.STATE_STREAMS + + return RC_OK + + # remove stream from port + def remove_stream (self, stream_id): + + if not stream_id in self.streams: + return self.err("stream {0} does not exists".format(stream_id)) + + params = {"handler": self.handler, + "port_id": self.port_id, + "stream_id": stream_id} + + + rc, data = self.transmit("remove_stream", params) + if not rc: + return self.err(data) + + self.streams[stream_id] = None + + return RC_OK + + # remove all the streams + def remove_all_streams (self): + for stream_id in self.streams.keys(): + rc = self.remove_stream(stream_id) + if rc.bad(): + return rc + + return RC_OK + + # start traffic + def start (self, mul): + if self.state == self.STATE_DOWN: + return self.err("Unable to start traffic - port is down") + + if self.state == self.STATE_IDLE: + return self.err("Unable to start traffic - no streams attached to port") + + if self.state == self.STATE_TX: + return self.err("Unable to start traffic - port is already transmitting") + + params = {"handler": self.handler, + "port_id": self.port_id, + "mul": mul} + + rc, data = self.transmit("remove_stream", params) + if not rc: + return self.err(data) + + self.state = self.STATE_TX + + return RC_OK + + def stop (self): + if (self.state != self.STATE_TX) and (self.state != self.STATE_PAUSE): + return self.err("Unable to stop traffic - port is down") + + params = {"handler": self.handler, + "port_id": self.port_id} + + rc, data = self.transmit("stop_traffic", params) + if not rc: + return self.err(data) + + # only valid state after stop + self.state = self.STREAMS + + return RC_OK + class CTRexStatelessClient(object): """docstring for CTRexStatelessClient""" - def __init__(self, username, server="localhost", sync_port=5050, async_port = 4500, virtual=False): + def __init__(self, username, server="localhost", sync_port = 5050, async_port = 4500, virtual=False): super(CTRexStatelessClient, self).__init__() self.user = username self.comm_link = CTRexStatelessClient.CCommLink(server, sync_port, virtual) self.verbose = False self._conn_handler = {} self._active_ports = set() - self._stats = CTRexStatsManager("port", "stream") self._system_info = None self._server_version = None self.__err_log = None self._async_client = CTRexAsyncClient(async_port) + self.connected = False - # ----- decorator methods ----- # - def acquired(func): - def wrapper_f(self, *args, **kwargs): - # print func.__name__ - # print args - # print kwargs - port_ids = kwargs.get("port_id") - # if not port_ids: - # # print "FROM ARGS!" - # # print args - # port_ids = args[0] - if isinstance(port_ids, int): - # make sure port_ids is a list - port_ids = [port_ids] - bad_ids = set() - # print "=============" - # print port_ids - for port_id in port_ids: - port_owned = self._conn_handler.get(port_id) - if not port_owned: - bad_ids.add(port_id) - # elif active_and_owned: # stronger condition than just owned, hence gets precedence - # if port_owned and port_id in self._active_ports: - # continue - # else: - # bad_ids.add(port_id) - else: - continue - if bad_ids: - # Some port IDs are not according to desires status - raise ValueError("The requested method ('{0}') cannot be invoked since port IDs {1} aren't " - "acquired".format(func.__name__, list(bad_ids))) - else: - return func(self, *args, **kwargs) - return wrapper_f - - def force_status(owned=True, active_and_owned=False): - def wrapper(func): - def wrapper_f(self, *args, **kwargs): - # print args - # print kwargs - port_ids = kwargs.get("port_id") - if not port_ids: - #print "FROM ARGS!" - #print args - port_ids = args[0] - if isinstance(port_ids, int): - # make sure port_ids is a list - port_ids = [port_ids] - bad_ids = set() - # print "=============" - # print port_ids - for port_id in port_ids: - port_owned = self._conn_handler.get(port_id) - if owned and not port_owned: - bad_ids.add(port_id) - elif active_and_owned: # stronger condition than just owned, hence gets precedence - if port_owned and port_id in self._active_ports: - continue - else: - bad_ids.add(port_id) - else: - continue - if bad_ids: - # Some port IDs are not according to desires status - raise ValueError("The requested method ('{0}') cannot be invoked since port IDs {1} are not " - "at allowed states".format(func.__name__, list(bad_ids))) - else: - return func(self, *args, **kwargs) - return wrapper_f - return wrapper - - @property - def system_info(self): - if not self._system_info: - rc, info = self.get_system_info() - if rc: - self._system_info = info - else: - self.__err_log = info - return self._system_info if self._system_info else "Unknown" + ############# helper functions section ############## - @property - def server_version(self): - if not self._server_version: - rc, ver_info = self.get_version() - if rc: - self._server_version = ver_info - else: - self.__err_log = ver_info - return self._server_version if self._server_version else "Unknown" + def __validate_port_list(self, port_id): + if isinstance(port_id, list) or isinstance(port_id, set): + # check each item of the sequence + return all([self._is_ports_valid(port) + for port in port_id]) + elif (isinstance(port_id, int)) and (port_id >= 0) and (port_id <= self.get_port_count()): + return True + else: + return False + + def __ports (self, port_id_list): + if port_id_list == None: + return range(0, self.get_port_count()) + + for port_id in port_id_list: + if not isinstance(port_id, int) or (port_id < 0) or (port_id > self.get_port_count()): + raise ValueError("bad port id {0}".format(port_id)) + + return [port_id_list] if isinstance(port_id_list, int) else port_id_list + + ############ boot up section ################ - def is_connected(self): - return self.comm_link.is_connected + # connection sequence + def connect (self): - # ----- user-access methods ----- # - def connect(self): - rc, err = self.comm_link.connect() + self.connected = False + + # connect + rc, data = self.comm_link.connect() if not rc: - return rc, err - return self._init_sync() + return RC_ERR(data) - def get_stats_async (self): - return self._async_client.get_stats() - def get_connection_port (self): - return self.comm_link.port + # cache system info + rc, data = self.transmit("get_system_info") + if not rc: + return RC_ERR(data) + + self.system_info = data + + # cache supported cmds + rc, data = self.transmit("get_supported_cmds") + if not rc: + return RC_ERR(data) + + self.supported_cmds = data + + # create ports + self.ports = [] + for port_id in xrange(0, self.get_port_count()): + self.ports.append(Port(port_id, self.user, self.transmit)) + + # acquire all ports + rc = self.acquire() + if rc.bad(): + return rc + + rc = self.sync_with_server() + if rc.bad(): + return rc + + self.connected = True + + return RC_OK + + def is_connected (self): + return self.connected + def disconnect(self): - return self.comm_link.disconnect() + self.connected = False + self.comm_link.disconnect() - def ping(self): - return self.transmit("ping") + + ########### cached queries (no server traffic) ########### def get_supported_cmds(self): - return self.transmit("get_supported_cmds") + return self.supported_cmds def get_version(self): - return self.transmit("get_version") + return self.server_version def get_system_info(self): - return self.transmit("get_system_info") + return self.system_info def get_port_count(self): return self.system_info.get("port_count") @@ -177,205 +360,167 @@ class CTRexStatelessClient(object): else: return port_ids - def sync_user(self, sync_streams=False): - return self.transmit("sync_user", {"user": self.user, "sync_streams": sync_streams}) + def get_stats_async (self): + return self._async_client.get_stats() + + def get_connection_port (self): + return self.comm_link.port def get_acquired_ports(self): - return self._conn_handler.keys() + return [port for port in self.ports if port.is_acquired()] + def get_active_ports(self): - return list(self._active_ports) + return [port for port in self.ports if port.is_active()] def set_verbose(self, mode): self.comm_link.set_verbose(mode) self.verbose = mode - def acquire(self, port_id, force=False): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") - if isinstance(port_id, list) or isinstance(port_id, set): - # handle as batch mode - port_ids = set(port_id) # convert to set to avoid duplications - commands = [RpcCmdData("acquire", {"port_id": p_id, "user": self.user, "force": force}) - for p_id in port_ids] - rc, resp_list = self.transmit_batch(commands) - if rc: - return self._process_batch_result(commands, resp_list, self._handle_acquire_response) - else: - params = {"port_id": port_id, - "user": self.user, - "force": force} - command = RpcCmdData("acquire", params) - return self._handle_acquire_response(command, - self.transmit(command.method, command.params), - self.default_success_test) - - @force_status(owned=True) - def release(self, port_id=None): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") - if isinstance(port_id, list) or isinstance(port_id, set): - # handle as batch mode - port_ids = set(port_id) # convert to set to avoid duplications - commands = [RpcCmdData("release", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) - for p_id in port_ids] - rc, resp_list = self.transmit_batch(commands) - if rc: - return self._process_batch_result(commands, resp_list, self._handle_release_response, - success_test=self.ack_success_test) - else: - self._conn_handler.pop(port_id) - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id} - command = RpcCmdData("release", params) - return self._handle_release_response(command, - self.transmit(command.method, command.params), - self.ack_success_test) + ############# server actions ################ - @acquired - def add_stream(self, stream_id, stream_obj, port_id=None): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") + # ping server + def ping(self): + rc, info = self.transmit("ping") + return RC(rc, info) + + + def sync_with_server(self, sync_streams=False): + rc, data = self.transmit("sync_user", {"user": self.user, "sync_streams": sync_streams}) + if not rc: + return RC_ERR(data) + + for port_info in data: + + rc = self.ports[port_info['port_id']].sync(port_info) + if rc.bad(): + return rc + + return RC_OK + + + + ########## port commands ############## + # acquire ports, if port_list is none - get all + def acquire (self, port_id_list = None, force = False): + port_id_list = self.__ports(port_id_list) + + rc_list = RC_LIST() + + for port_id in port_id_list: + rc = self.ports[port_id].acquire(force) + rc_list.add(rc) + + return rc_list + + # release ports + def release (self, port_id_list = None): + port_id_list = self.__ports(port_id_list) + + rc_list = RC_LIST() + + for port_id in port_id_list: + rc, msg = self.ports[port_id].release(force) + rc_list.add(rc) + + return rc_list + + + def add_stream(self, stream_id, stream_obj, port_id_list = None): assert isinstance(stream_obj, CStream) - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id, - "stream_id": stream_id, - "stream": stream_obj.dump()} - return self.transmit("add_stream", params) - @acquired - def add_stream_pack(self, stream_pack_list, port_id=None): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") + port_id_list = self.__ports(port_id_list) + + rc_list = RC_LIST() + + for port_id in port_id_list: + rc = self.ports[port_id].add_stream(stream_id, stream_obj) + rc_list.add(rc) + + return rc_list + + + def add_stream_pack(self, stream_pack_list, port_id_list = None): + + port_id_list = self.__ports(port_id_list) + + rc_list = RC_LIST() - # since almost every run contains more than one transaction with server, handle all as batch mode - port_ids = set(port_id) # convert to set to avoid duplications - commands = [] for stream_pack in stream_pack_list: - commands.extend([RpcCmdData("add_stream", {"port_id": p_id, - "handler": self._conn_handler.get(p_id), - "stream_id": stream_pack.stream_id, - "stream": stream_pack.stream} - ) - for p_id in port_ids] - ) - res_ok, resp_list = self.transmit_batch(commands) - if not res_ok: - return res_ok, resp_list - - return self._process_batch_result(commands, resp_list, self._handle_add_stream_response, - success_test=self.ack_success_test) - - @force_status(owned=True) - def remove_stream(self, stream_id, port_id=None): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id, - "stream_id": stream_id} - return self.transmit("remove_stream", params) + rc = self.add_stream(stream_pack.stream_id, stream_pack.stream, port_id_list) + rc_list.add(rc) + + return rc_list - def remove_all_streams(self, port_id=None): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") - if isinstance(port_id, list) or isinstance(port_id, set): - # handle as batch mode - port_ids = set(port_id) # convert to set to avoid duplications - commands = [RpcCmdData("remove_all_streams", {"port_id": p_id, "handler": self._conn_handler.get(p_id)}) - for p_id in port_ids] - rc, resp_list = self.transmit_batch(commands) - if rc: - return self._process_batch_result(commands, resp_list, self._handle_remove_streams_response, - success_test=self.ack_success_test) - else: - params = {"port_id": port_id, - "handler": self._conn_handler.get(port_id)} - command = RpcCmdData("remove_all_streams", params) - return self._handle_remove_streams_response(command, - self.transmit(command.method, command.params), - self.ack_success_test) - pass - @force_status(owned=True)#, active_and_owned=True) - def get_all_streams(self, port_id, get_pkt = False): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id, - "get_pkt": get_pkt} - return self.transmit("get_all_streams", params) + def remove_stream(self, stream_id, port_id_list = None): + port_id_list = self.__ports(port_id_list) + + rc_list = RC_LIST() + + for port_id in port_id_list: + rc = self.ports[port_id].remove_stream(stream_id) + rc_list.add(rc) + + return rc_list + - @force_status(owned=True)#, active_and_owned=True) - def get_stream_id_list(self, port_id=None): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id} - return self.transmit("get_stream_list", params) - @force_status(owned=True, active_and_owned=True) + def remove_all_streams(self, port_id_list = None): + port_id_list = self.__ports(port_id_list) + + rc_list = RC_LIST() + + for port_id in port_id_list: + rc = self.ports[port_id].remove_all_streams() + rc_list.add(rc) + + return rc_list + + def get_stream(self, stream_id, port_id, get_pkt = False): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id, - "stream_id": stream_id, - "get_pkt": get_pkt} - return self.transmit("get_stream_list", params) - def start_traffic(self, multiplier, port_id=None): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") - if isinstance(port_id, list) or isinstance(port_id, set): - # handle as batch mode - port_ids = set(port_id) # convert to set to avoid duplications - commands = [RpcCmdData("start_traffic", {"handler": self._conn_handler.get(p_id), - "port_id": p_id, - "mul": multiplier}) - for p_id in port_ids] - rc, resp_list = self.transmit_batch(commands) - if rc: - return self._process_batch_result(commands, resp_list, self._handle_start_traffic_response, - success_test=self.ack_success_test) - else: - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id, - "mul": multiplier} - command = RpcCmdData("start_traffic", params) - return self._handle_start_traffic_response(command, - self.transmit(command.method, command.params), - self.ack_success_test) - - def stop_traffic(self, port_id=None): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") - if isinstance(port_id, list) or isinstance(port_id, set): - # handle as batch mode - port_ids = set(port_id) # convert to set to avoid duplications - if not port_ids: - # don't invoke if port ids is empty - return True, [] - commands = [RpcCmdData("stop_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) - for p_id in port_ids] - rc, resp_list = self.transmit_batch(commands) - if rc: - return self._process_batch_result(commands, resp_list, self._handle_stop_traffic_response, - success_test=self.ack_success_test) - else: - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id} - command = RpcCmdData("stop_traffic", params) - return self._handle_stop_traffic_response(command, - self.transmit(command.method, command.params), - self.ack_success_test) + return self.ports[port_id].get_stream(stream_id) + + + def get_all_streams(self, port_id, get_pkt = False): + + return self.ports[port_id].get_all_streams() + + + def get_stream_id_list(self, port_id): + + return self.ports[port_id].get_stream_id_list() + + + def start_traffic (self, multiplier, port_id_list = None): + + port_id_list = self.__ports(port_id_list) + + rc_list = RC_LIST() + + for port_id in port_id_list: + rc = self.ports[port_id].start(multiplier) + rc_list.add(rc) + + return rc_list + + + + def stop_traffic (self, port_id_list = None): + + port_id_list = self.__ports(port_id_list) + + rc_list = RC_LIST() + + for port_id in port_id_list: + rc = self.ports[port_id].stop() + rc_list.add(rc) + + return rc_list -# def get_global_stats(self): -# command = RpcCmdData("get_global_stats", {}) -# return self._handle_get_global_stats_response(command, self.transmit(command.method, command.params)) -# # return self.transmit("get_global_stats") - @force_status(owned=True, active_and_owned=True) def get_port_stats(self, port_id=None): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") @@ -393,7 +538,6 @@ class CTRexStatelessClient(object): command = RpcCmdData("get_port_stats", params) return self._handle_get_port_stats_response(command, self.transmit(command.method, command.params)) - @force_status(owned=True, active_and_owned=True) def get_stream_stats(self, port_id=None): if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") @@ -411,26 +555,6 @@ class CTRexStatelessClient(object): command = RpcCmdData("get_stream_stats", params) return self._handle_get_stream_stats_response(command, self.transmit(command.method, command.params)) - # ----- internal methods ----- # - def _init_sync(self): - # get server version and system info - err = False - if self.server_version == "Unknown" or self.system_info == "Unknown": - self.disconnect() - return False, self.__err_log - # sync with previous session - res_ok, port_info = self.sync_user() - if not res_ok: - self.disconnect() - return False, port_info - else: - # handle sync data - for port in port_info: - self._conn_handler[port.get("port_id")] = port.get("handler") - if port.get("state") == "transmitting": - # port is active - self._active_ports.add(port.get("port_id")) - return True, "" def transmit(self, method_name, params={}): @@ -439,17 +563,6 @@ class CTRexStatelessClient(object): def transmit_batch(self, batch_list): return self.comm_link.transmit_batch(batch_list) - @staticmethod - def _object_decoder(obj_type, obj_data): - if obj_type == "global": - return CGlobalStats(**obj_data) - elif obj_type == "port": - return CPortStats(**obj_data) - elif obj_type == "stream": - return CStreamStats(**obj_data) - else: - # Do not serialize the data into class - return obj_data @staticmethod def default_success_test(result_obj): @@ -474,35 +587,37 @@ class CTRexStatelessClient(object): # def cmd_reset (self, annotate_func): - ports = self.get_port_ids() # sync with the server - rc, log = self._init_sync() - annotate_func("Syncing with the server:", rc, log) - if not rc: - return False - + rc = self.sync_with_server() + annotate_func("Syncing with the server:", rc.good(), rc.err()) + if rc.bad(): + return rc # force acquire all ports - rc, log = self.acquire(ports, force = True) - annotate_func("Force acquiring all ports:", rc, log) - if not rc: - return False + rc = self.acquire(force = True) + annotate_func("Force acquiring all ports:", rc.good(), rc.err()) + if rc.bad(): + return rc - # force stop - rc, log = self.stop_traffic(ports) - annotate_func("Stop traffic on all ports:", rc, log) - if not rc: - return False + + # force stop all ports + port_id_list = self.get_active_ports() + rc = self.stop_traffic(port_id_list) + annotate_func("Stop traffic on all ports:", rc.good(), rc.err()) + if rc.bad(): + return rc + + return # remove all streams - rc, log = self.remove_all_streams(ports) - annotate_func("Removing all streams from all ports:", rc, log) - if not rc: - return False + rc = self.remove_all_streams(ports) + annotate_func("Removing all streams from all ports:", rc.good(), rc.err()) + if rc.bad(): + return rc # TODO: clear stats - return True + return RC_OK # stop cmd @@ -511,7 +626,7 @@ class CTRexStatelessClient(object): # find the relveant ports active_ports = set(self.get_active_ports()).intersection(ports) if not active_ports: - annotate_func("No active traffic on porivded ports") + annotate_func("No active traffic on porvided ports") return True rc, log = self.stop_traffic(active_ports) @@ -524,7 +639,7 @@ class CTRexStatelessClient(object): # start cmd def cmd_start (self, ports, stream_list, mult, force, annotate_func): - if force: + if (force and set(self.get_active_ports()).intersection(ports)): rc = self.cmd_stop(ports, annotate_func) if not rc: return False @@ -626,15 +741,6 @@ class CTRexStatelessClient(object): else: return False - def _is_ports_valid(self, port_id): - if isinstance(port_id, list) or isinstance(port_id, set): - # check each item of the sequence - return all([self._is_ports_valid(port) - for port in port_id]) - elif (isinstance(port_id, int)) and (port_id >= 0) and (port_id <= self.get_port_count()): - return True - else: - return False def _process_batch_result(self, req_list, resp_list, handler_func=None, success_test=default_success_test): res_ok = True diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index 58491aba..077c82ad 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -110,7 +110,7 @@ class JsonRpcClient(object): return id, msg - def invoke_rpc_method (self, method_name, params = {}, block = False): + def invoke_rpc_method (self, method_name, params = {}, block = True): if not self.connected: return False, "Not connected to server" @@ -120,7 +120,7 @@ class JsonRpcClient(object): # low level send of string message - def send_raw_msg (self, msg, block = False): + def send_raw_msg (self, msg, block = True): self.verbose_msg("Sending Request To Server:\n\n" + self.pretty_json(msg) + "\n") if block: @@ -248,272 +248,3 @@ class JsonRpcClient(object): if hasattr(self, "context"): self.context.destroy(linger=0) -# MOVE THIS TO DAN'S FILE -class TrexStatelessClient(JsonRpcClient): - - def __init__ (self, server, port, user): - - super(TrexStatelessClient, self).__init__(server, port) - - self.user = user - self.port_handlers = {} - - self.supported_cmds = [] - self.system_info = None - self.server_version = None - - - def whoami (self): - return self.user - - def ping_rpc_server(self): - - return self.invoke_rpc_method("ping", block = False) - - def get_rpc_server_version (self): - return self.server_version - - def get_system_info (self): - if not self.system_info: - return {} - - return self.system_info - - def get_supported_cmds(self): - if not self.supported_cmds: - return {} - - return self.supported_cmds - - def get_port_count (self): - if not self.system_info: - return 0 - - return self.system_info["port_count"] - - # sync the client with all the server required data - def sync (self): - - # get server version - rc, msg = self.invoke_rpc_method("get_version") - if not rc: - self.disconnect() - return rc, msg - - self.server_version = msg - - # get supported commands - rc, msg = self.invoke_rpc_method("get_supported_cmds") - if not rc: - self.disconnect() - return rc, msg - - self.supported_cmds = [str(x) for x in msg if x] - - # get system info - rc, msg = self.invoke_rpc_method("get_system_info") - if not rc: - self.disconnect() - return rc, msg - - self.system_info = msg - - return True, "" - - def connect (self): - rc, err = super(TrexStatelessClient, self).connect() - if not rc: - return rc, err - - return self.sync() - - - # take ownership over ports - def take_ownership (self, port_id_array, force = False): - if not self.connected: - return False, "Not connected to server" - - batch = self.create_batch() - - for port_id in port_id_array: - batch.add("acquire", params = {"port_id":port_id, "user":self.user, "force":force}) - - rc, resp_list = batch.invoke() - if not rc: - return rc, resp_list - - for i, rc in enumerate(resp_list): - if rc[0]: - self.port_handlers[port_id_array[i]] = rc[1] - - return True, resp_list - - - def release_ports (self, port_id_array): - batch = self.create_batch() - - for port_id in port_id_array: - - # let the server handle un-acquired errors - if self.port_handlers.get(port_id): - handler = self.port_handlers[port_id] - else: - handler = "" - - batch.add("release", params = {"port_id":port_id, "handler":handler}) - - - rc, resp_list = batch.invoke() - if not rc: - return rc, resp_list - - for i, rc in enumerate(resp_list): - if rc[0]: - self.port_handlers.pop(port_id_array[i]) - - return True, resp_list - - def get_owned_ports (self): - return self.port_handlers.keys() - - # fetch port stats - def get_port_stats (self, port_id_array): - if not self.connected: - return False, "Not connected to server" - - batch = self.create_batch() - - # empty list means all - if port_id_array == []: - port_id_array = list([x for x in xrange(0, self.system_info["port_count"])]) - - for port_id in port_id_array: - - # let the server handle un-acquired errors - if self.port_handlers.get(port_id): - handler = self.port_handlers[port_id] - else: - handler = "" - - batch.add("get_port_stats", params = {"port_id":port_id, "handler":handler}) - - - rc, resp_list = batch.invoke() - - return rc, resp_list - - # snapshot will take a snapshot of all your owned ports for streams and etc. - def snapshot(self): - - - if len(self.get_owned_ports()) == 0: - return {} - - snap = {} - - batch = self.create_batch() - - for port_id in self.get_owned_ports(): - - batch.add("get_port_stats", params = {"port_id": port_id, "handler": self.port_handlers[port_id]}) - batch.add("get_stream_list", params = {"port_id": port_id, "handler": self.port_handlers[port_id]}) - - rc, resp_list = batch.invoke() - if not rc: - return rc, resp_list - - # split the list to 2s - index = 0 - for port_id in self.get_owned_ports(): - if not resp_list[index] or not resp_list[index + 1]: - snap[port_id] = None - continue - - # fetch the first two - stats = resp_list[index][1] - stream_list = resp_list[index + 1][1] - - port = {} - port['status'] = stats['status'] - port['stream_list'] = [] - - # get all the streams - if len(stream_list) > 0: - batch = self.create_batch() - for stream_id in stream_list: - batch.add("get_stream", params = {"port_id": port_id, "stream_id": stream_id, "handler": self.port_handlers[port_id]}) - - rc, stream_resp_list = batch.invoke() - if not rc: - port = {} - - port['streams'] = {} - for i, resp in enumerate(stream_resp_list): - if resp[0]: - port['streams'][stream_list[i]] = resp[1] - - snap[port_id] = port - - # move to next one - index += 2 - - - return snap - - # add stream - # def add_stream (self, port_id, stream_id, isg, next_stream_id, packet, vm=[]): - # if not port_id in self.get_owned_ports(): - # return False, "Port {0} is not owned... please take ownership before adding streams".format(port_id) - # - # handler = self.port_handlers[port_id] - # - # stream = {} - # stream['enabled'] = True - # stream['self_start'] = True - # stream['isg'] = isg - # stream['next_stream_id'] = next_stream_id - # stream['packet'] = {} - # stream['packet']['binary'] = packet - # stream['packet']['meta'] = "" - # stream['vm'] = vm - # stream['rx_stats'] = {} - # stream['rx_stats']['enabled'] = False - # - # stream['mode'] = {} - # stream['mode']['type'] = 'continuous' - # stream['mode']['pps'] = 10.0 - # - # params = {} - # params['handler'] = handler - # params['stream'] = stream - # params['port_id'] = port_id - # params['stream_id'] = stream_id - # - # print params - # return self.invoke_rpc_method('add_stream', params = params) - - def add_stream(self, port_id_array, stream_pack_list): - batch = self.create_batch() - - for port_id in port_id_array: - for stream_pack in stream_pack_list: - params = {"port_id": port_id, - "handler": self.port_handlers[port_id], - "stream_id": stream_pack.stream_id, - "stream": stream_pack.stream} - batch.add("add_stream", params=params) - rc, resp_list = batch.invoke() - if not rc: - return rc, resp_list - - for i, rc in enumerate(resp_list): - if rc[0]: - print "Stream {0} - {1}".format(i, rc[1]) - # self.port_handlers[port_id_array[i]] = rc[1] - - return True, resp_list - - # return self.invoke_rpc_method('add_stream', params = params) - -if __name__ == "__main__": - pass \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 06ae762a..2be643ab 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -160,7 +160,51 @@ class TRexConsole(cmd.Cmd): for x in os.listdir(path) if x.startswith(start_string)] + # annotation method + @staticmethod + def annotate (desc, rc = None, err_log = None, ext_err_msg = None): + print format_text('\n{:<40}'.format(desc), 'bold'), + if rc == None: + print "\n" + return + + if rc == False: + # do we have a complex log object ? + if isinstance(err_log, list): + print "" + for func in err_log: + if func: + print func + print "" + + elif isinstance(err_log, str): + print "\n" + err_log + "\n" + + print format_text("[FAILED]\n", 'red', 'bold') + if ext_err_msg: + print format_text(ext_err_msg + "\n", 'blue', 'bold') + + return False + + else: + print format_text("[SUCCESS]\n", 'green', 'bold') + return True + + ####################### shell commands ####################### + def do_ping (self, line): + '''Ping the server\n''' + + rc = self.stateless_client.ping() + if rc.good(): + print format_text("[SUCCESS]\n", 'green', 'bold') + else: + print "\n*** " + rc.err() + "\n" + print format_text("[FAILED]\n", 'red', 'bold') + return + + def do_test (self, line): + print self.stateless_client.get_acquired_ports() # set verbose on / off def do_verbose(self, line): @@ -171,65 +215,41 @@ class TRexConsole(cmd.Cmd): elif line == "on": self.verbose = True self.stateless_client.set_verbose(True) - print green("\nverbose set to on\n") + print format_text("\nverbose set to on\n", 'green', 'bold') elif line == "off": self.verbose = False self.stateless_client.set_verbose(False) - print green("\nverbose set to off\n") + print format_text("\nverbose set to off\n", 'green', 'bold') else: - print magenta("\nplease specify 'on' or 'off'\n") + print format_text("\nplease specify 'on' or 'off'\n", 'bold') + ############### connect def do_connect (self, line): '''Connects to the server\n''' - res_ok, msg = self.stateless_client.connect() - if res_ok: + rc = self.stateless_client.connect() + if rc.good(): print format_text("[SUCCESS]\n", 'green', 'bold') else: - print "\n*** " + msg + "\n" + print "\n*** " + rc.err() + "\n" print format_text("[FAILED]\n", 'red', 'bold') return - self.supported_rpc = self.stateless_client.get_supported_cmds().data - if self.acquire_all_ports: - res_ok, log = self.stateless_client.acquire(self.stateless_client.get_port_ids()) - if not res_ok: - print "\n*** Failed to acquire all ports... exiting...""" + def do_disconnect (self, line): + '''Disconnect from the server\n''' - @staticmethod - def annotate (desc, rc = None, err_log = None, ext_err_msg = None): - print format_text('\n{:<40}'.format(desc), 'bold'), - if rc == None: - print "\n" + if not self.stateless_client.is_connected(): + print "Not connected to server\n" return - if rc == False: - # do we have a complex log object ? - if isinstance(err_log, list): - print "" - for func in err_log: - if func: - print func - print "" - - elif isinstance(err_log, str): - print "\n" + err_log + "\n" - - print format_text("[FAILED]\n", 'red', 'bold') - if ext_err_msg: - print format_text(ext_err_msg + "\n", 'blue', 'bold') - - return False - - else: - print format_text("[SUCCESS]\n", 'green', 'bold') - return True - + self.stateless_client.disconnect() + print format_text("[SUCCESS]\n", 'green', 'bold') + ############### start def complete_start(self, text, line, begidx, endidx): -- cgit From 513581840e5787e73161de049aa59552f23e719d Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 12 Nov 2015 18:28:21 +0200 Subject: modifying stateless client to a simpler lightweight module --- .../client/trex_stateless_client.py | 406 +++++++-------------- .../trex_control_plane/console/parsing_opts.py | 4 +- .../trex_control_plane/console/trex_console.py | 6 +- 3 files changed, 144 insertions(+), 272 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 5a7b1873..93b36f82 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -12,6 +12,7 @@ import json from common.trex_stats import * from common.trex_streams import * from collections import namedtuple +from common.text_opts import * from trex_async_client import CTRexAsyncClient @@ -27,50 +28,48 @@ class RpcResponseStatus(namedtuple('RpcResponseStatus', ['success', 'id', 'msg'] # simple class to represent complex return value class RC: - def __init__ (self, rc, data): - self.rc = rc - self.data = data + def __init__ (self, rc = None, data = None): + self.rc_list = [] + + if (rc != None) and (data != None): + tuple_rc = namedtuple('RC', ['rc', 'data']) + self.rc_list.append(tuple_rc(rc, data)) + + def add (self, rc): + self.rc_list += rc.rc_list def good (self): - return self.rc + return all([x.rc for x in self.rc_list]) def bad (self): - return not self.rc + return not self.good() def data (self): - if self.good(): - return self.data - else: - return "" + return all([x.data if x.rc else "" for x in self.rc_list]) def err (self): - if self.bad(): - return self.data - else: - return "" + return all([x.data if not x.rc else "" for x in self.rc_list]) -RC_OK = RC(True, "") -def RC_ERR (err): - return RC(False, err) + def annotate (self, desc): + print format_text('\n{:<40}'.format(desc), 'bold'), -class RC_LIST: - def __init__ (self): - self.rc_list = [] + if self.bad(): + # print all the errors + for x in self.rc_list: + if not x.rc: + print format_text("\n{0}".format(x.data), 'bold') - def add (self, rc): - self.rc_list.append(rc) + print format_text("[FAILED]\n", 'red', 'bold') - def good(self): - return all([x.good() for x in self.rc_list]) - def bad (self): - not self.good() + else: + print format_text("[SUCCESS]\n", 'green', 'bold') - def data (self): - return [x.data() for x in self.rc_list] - def err (self): - return [x.err() for x in self.rc_list] +def RC_OK(): + return RC(True, "") +def RC_ERR (err): + return RC(False, err) # describes a single port @@ -104,7 +103,7 @@ class Port: rc = self.transmit(command.method, command.params) if rc.success: self.handler = rc.data - return RC_OK + return RC_OK() else: return RC_ERR(rc.data) @@ -118,7 +117,7 @@ class Port: rc = self.transmit(command.method, command.params) if rc.success: self.handler = rc.data - return RC_OK + return RC_OK() else: return RC_ERR(rc.data) @@ -145,13 +144,13 @@ class Port: else: raise Exception("port {0}: bad state received from server '{1}'".format(self.port_id, sync_data['state'])) - return RC_OK + return RC_OK() # return TRUE if write commands def is_port_writeable (self): # operations on port can be done on state idle or state sreams - return ((self.state == STATE_IDLE) or (self.state == STATE_STREAMS)) + return ((self.state == self.STATE_IDLE) or (self.state == self.STATE_STREAMS)) # add stream to the port def add_stream (self, stream_id, stream_obj): @@ -163,11 +162,12 @@ class Port: params = {"handler": self.handler, "port_id": self.port_id, "stream_id": stream_id, - "stream": stream_obj.dump()} + "stream": stream_obj} rc, data = self.transmit("add_stream", params) if not rc: - return self.err(data) + r = self.err(data) + print r.good() # add the stream self.streams[stream_id] = stream_obj @@ -175,7 +175,7 @@ class Port: # the only valid state now self.state = self.STATE_STREAMS - return RC_OK + return RC_OK() # remove stream from port def remove_stream (self, stream_id): @@ -194,16 +194,21 @@ class Port: self.streams[stream_id] = None - return RC_OK + return RC_OK() # remove all the streams def remove_all_streams (self): - for stream_id in self.streams.keys(): - rc = self.remove_stream(stream_id) - if rc.bad(): - return rc - return RC_OK + params = {"handler": self.handler, + "port_id": self.port_id} + + rc, data = self.transmit("remove_all_streams", params) + if not rc: + return self.err(data) + + self.streams = {} + + return RC_OK() # start traffic def start (self, mul): @@ -220,17 +225,20 @@ class Port: "port_id": self.port_id, "mul": mul} - rc, data = self.transmit("remove_stream", params) + rc, data = self.transmit("start_traffic", params) if not rc: return self.err(data) self.state = self.STATE_TX - return RC_OK + return RC_OK() + + # stop traffic + # with force ignores the cached state and sends the command + def stop (self, force = False): - def stop (self): - if (self.state != self.STATE_TX) and (self.state != self.STATE_PAUSE): - return self.err("Unable to stop traffic - port is down") + if (not force) and (self.state != self.STATE_TX) and (self.state != self.STATE_PAUSE): + return self.err("port is not transmitting") params = {"handler": self.handler, "port_id": self.port_id} @@ -240,9 +248,10 @@ class Port: return self.err(data) # only valid state after stop - self.state = self.STREAMS + self.state = self.STATE_STREAMS + + return RC_OK() - return RC_OK class CTRexStatelessClient(object): @@ -265,7 +274,7 @@ class CTRexStatelessClient(object): ############# helper functions section ############## - def __validate_port_list(self, port_id): + def validate_port_list(self, port_id): if isinstance(port_id, list) or isinstance(port_id, set): # check each item of the sequence return all([self._is_ports_valid(port) @@ -275,15 +284,25 @@ class CTRexStatelessClient(object): else: return False + # some preprocessing for port argument def __ports (self, port_id_list): + + # none means all if port_id_list == None: return range(0, self.get_port_count()) + # always list + if isinstance(port_id_list, int): + port_id_list = [port_id_list] + + if not isinstance(port_id_list, list): + raise ValueError("bad port id list: {0}".format(port_id_list)) + for port_id in port_id_list: if not isinstance(port_id, int) or (port_id < 0) or (port_id > self.get_port_count()): raise ValueError("bad port id {0}".format(port_id)) - return [port_id_list] if isinstance(port_id_list, int) else port_id_list + return port_id_list ############ boot up section ################ @@ -297,7 +316,6 @@ class CTRexStatelessClient(object): if not rc: return RC_ERR(data) - # cache system info rc, data = self.transmit("get_system_info") if not rc: @@ -328,7 +346,7 @@ class CTRexStatelessClient(object): self.connected = True - return RC_OK + return RC_OK() def is_connected (self): return self.connected @@ -367,11 +385,11 @@ class CTRexStatelessClient(object): return self.comm_link.port def get_acquired_ports(self): - return [port for port in self.ports if port.is_acquired()] + return [port.port_id for port in self.ports if port.is_acquired()] def get_active_ports(self): - return [port for port in self.ports if port.is_active()] + return [port.port_id for port in self.ports if port.is_active()] def set_verbose(self, mode): self.comm_link.set_verbose(mode) @@ -396,7 +414,7 @@ class CTRexStatelessClient(object): if rc.bad(): return rc - return RC_OK + return RC_OK() @@ -405,78 +423,71 @@ class CTRexStatelessClient(object): def acquire (self, port_id_list = None, force = False): port_id_list = self.__ports(port_id_list) - rc_list = RC_LIST() + rc = RC() for port_id in port_id_list: - rc = self.ports[port_id].acquire(force) - rc_list.add(rc) - - return rc_list + rc.add(self.ports[port_id].acquire(force)) + + return rc # release ports def release (self, port_id_list = None): port_id_list = self.__ports(port_id_list) - rc_list = RC_LIST() + rc = RC() for port_id in port_id_list: - rc, msg = self.ports[port_id].release(force) - rc_list.add(rc) + rc.add(self.ports[port_id].release(force)) - return rc_list + return rc def add_stream(self, stream_id, stream_obj, port_id_list = None): - assert isinstance(stream_obj, CStream) port_id_list = self.__ports(port_id_list) - rc_list = RC_LIST() + rc = RC() for port_id in port_id_list: - rc = self.ports[port_id].add_stream(stream_id, stream_obj) - rc_list.add(rc) + rc.add(self.ports[port_id].add_stream(stream_id, stream_obj)) - return rc_list + return rc def add_stream_pack(self, stream_pack_list, port_id_list = None): port_id_list = self.__ports(port_id_list) - rc_list = RC_LIST() + rc = RC() for stream_pack in stream_pack_list: - rc = self.add_stream(stream_pack.stream_id, stream_pack.stream, port_id_list) - rc_list.add(rc) + rc.add(self.add_stream(stream_pack.stream_id, stream_pack.stream, port_id_list)) - return rc_list + return rc def remove_stream(self, stream_id, port_id_list = None): port_id_list = self.__ports(port_id_list) - rc_list = RC_LIST() + rc = RC() for port_id in port_id_list: - rc = self.ports[port_id].remove_stream(stream_id) - rc_list.add(rc) + rc.add(self.ports[port_id].remove_stream(stream_id)) - return rc_list + return rc def remove_all_streams(self, port_id_list = None): port_id_list = self.__ports(port_id_list) - rc_list = RC_LIST() + rc = RC() for port_id in port_id_list: - rc = self.ports[port_id].remove_all_streams() - rc_list.add(rc) + rc.add(self.ports[port_id].remove_all_streams()) - return rc_list + return rc def get_stream(self, stream_id, port_id, get_pkt = False): @@ -498,85 +509,36 @@ class CTRexStatelessClient(object): port_id_list = self.__ports(port_id_list) - rc_list = RC_LIST() + rc = RC() for port_id in port_id_list: - rc = self.ports[port_id].start(multiplier) - rc_list.add(rc) + rc.add(self.ports[port_id].start(multiplier)) - return rc_list + return rc - def stop_traffic (self, port_id_list = None): + def stop_traffic (self, port_id_list = None, force = False): port_id_list = self.__ports(port_id_list) - - rc_list = RC_LIST() + rc = RC() for port_id in port_id_list: - rc = self.ports[port_id].stop() - rc_list.add(rc) + rc.add(self.ports[port_id].stop(force)) - return rc_list + return rc def get_port_stats(self, port_id=None): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") - if isinstance(port_id, list) or isinstance(port_id, set): - # handle as batch mode - port_ids = set(port_id) # convert to set to avoid duplications - commands = [RpcCmdData("get_port_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) - for p_id in port_ids] - rc, resp_list = self.transmit_batch(commands) - if rc: - self._process_batch_result(commands, resp_list, self._handle_get_port_stats_response) - else: - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id} - command = RpcCmdData("get_port_stats", params) - return self._handle_get_port_stats_response(command, self.transmit(command.method, command.params)) + pass def get_stream_stats(self, port_id=None): - if not self._is_ports_valid(port_id): - raise ValueError("Provided illegal port id input") - if isinstance(port_id, list) or isinstance(port_id, set): - # handle as batch mode - port_ids = set(port_id) # convert to set to avoid duplications - commands = [RpcCmdData("get_stream_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) - for p_id in port_ids] - rc, resp_list = self.transmit_batch(commands) - if rc: - self._process_batch_result(commands, resp_list, self._handle_get_stream_stats_response) - else: - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id} - command = RpcCmdData("get_stream_stats", params) - return self._handle_get_stream_stats_response(command, self.transmit(command.method, command.params)) - + pass def transmit(self, method_name, params={}): return self.comm_link.transmit(method_name, params) - def transmit_batch(self, batch_list): - return self.comm_link.transmit_batch(batch_list) - - - @staticmethod - def default_success_test(result_obj): - if result_obj.success: - return True - else: - return False - - @staticmethod - def ack_success_test(result_obj): - if result_obj.success and result_obj.data == "ACK": - return True - else: - return False ######################### Console (high level) API ######################### @@ -585,181 +547,91 @@ class CTRexStatelessClient(object): # acquire, stop, remove streams and clear stats # # - def cmd_reset (self, annotate_func): + def cmd_reset (self): # sync with the server rc = self.sync_with_server() - annotate_func("Syncing with the server:", rc.good(), rc.err()) + rc.annotate("Syncing with the server:") if rc.bad(): return rc - # force acquire all ports rc = self.acquire(force = True) - annotate_func("Force acquiring all ports:", rc.good(), rc.err()) + rc.annotate("Force acquiring all ports:") if rc.bad(): return rc # force stop all ports - port_id_list = self.get_active_ports() - rc = self.stop_traffic(port_id_list) - annotate_func("Stop traffic on all ports:", rc.good(), rc.err()) + rc = self.stop_traffic(self.get_port_ids(), True) + rc.annotate("Stop traffic on all ports:") if rc.bad(): return rc - return # remove all streams - rc = self.remove_all_streams(ports) - annotate_func("Removing all streams from all ports:", rc.good(), rc.err()) + rc = self.remove_all_streams(self.get_port_ids()) + rc.annotate("Removing all streams from all ports:") if rc.bad(): return rc # TODO: clear stats - return RC_OK + return RC_OK() # stop cmd - def cmd_stop (self, ports, annotate_func): + def cmd_stop (self, port_id_list): # find the relveant ports - active_ports = set(self.get_active_ports()).intersection(ports) + active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) + if not active_ports: - annotate_func("No active traffic on porvided ports") + print format_text("No active traffic on porvided ports", 'bold') return True - rc, log = self.stop_traffic(active_ports) - annotate_func("Stopping traffic on ports {0}:".format([port for port in active_ports]), rc, log) - if not rc: + rc = self.stop_traffic(active_ports) + rc.annotate("Stopping traffic on port(s) {0}:".format(port_id_list)) + if rc.bad(): return False return True # start cmd - def cmd_start (self, ports, stream_list, mult, force, annotate_func): + def cmd_start (self, port_id_list, stream_list, mult, force): - if (force and set(self.get_active_ports()).intersection(ports)): - rc = self.cmd_stop(ports, annotate_func) - if not rc: - return False + active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) - rc, log = self.remove_all_streams(ports) - annotate_func("Removing all streams from ports {0}:".format([port for port in ports]), rc, log, - "Please either retry with --force or stop traffic") - if not rc: - return False + if active_ports: + if not force: + print format_text("Port(s) {0} are active - please stop them or add '--force'".format(active_ports), 'bold') + return False + else: + rc = self.cmd_stop(active_ports) + if not rc: + return False - rc, log = self.add_stream_pack(stream_list.compiled, port_id= ports) - annotate_func("Attaching streams to port {0}:".format([port for port in ports]), rc, log) - if not rc: - return False - # finally, start the traffic - rc, log = self.start_traffic(mult, ports) - annotate_func("Starting traffic on ports {0}:".format([port for port in ports]), rc, log) - if not rc: + rc = self.remove_all_streams(port_id_list) + rc.annotate("Removing all streams from ports {0}:".format(port_id_list)) + if rc.bad(): return False - return True - - # ----- handler internal methods ----- # - def _handle_general_response(self, request, response, msg, success_test=None): - port_id = request.params.get("port_id") - if not success_test: - success_test = self.default_success_test - if success_test(response): - self._conn_handler[port_id] = response.data - return RpcResponseStatus(True, port_id, msg) - else: - return RpcResponseStatus(False, port_id, response.data) - - def _handle_acquire_response(self, request, response, success_test): - port_id = request.params.get("port_id") - if success_test(response): - self._conn_handler[port_id] = response.data - return RpcResponseStatus(True, port_id, "Acquired") - else: - return RpcResponseStatus(False, port_id, response.data) - - def _handle_add_stream_response(self, request, response, success_test): - port_id = request.params.get("port_id") - stream_id = request.params.get("stream_id") - if success_test(response): - return RpcResponseStatus(True, port_id, "Stream {0} added".format(stream_id)) - else: - return RpcResponseStatus(False, port_id, response.data) - - def _handle_remove_streams_response(self, request, response, success_test): - port_id = request.params.get("port_id") - if success_test(response): - return RpcResponseStatus(True, port_id, "Removed") - else: - return RpcResponseStatus(False, port_id, response.data) - - def _handle_release_response(self, request, response, success_test): - port_id = request.params.get("port_id") - if success_test(response): - del self._conn_handler[port_id] - return RpcResponseStatus(True, port_id, "Released") - else: - return RpcResponseStatus(False, port_id, response.data) - - def _handle_start_traffic_response(self, request, response, success_test): - port_id = request.params.get("port_id") - if success_test(response): - self._active_ports.add(port_id) - return RpcResponseStatus(True, port_id, "Traffic started") - else: - return RpcResponseStatus(False, port_id, response.data) - - def _handle_stop_traffic_response(self, request, response, success_test): - port_id = request.params.get("port_id") - if success_test(response): - if port_id in self._active_ports: - self._active_ports.remove(port_id) - return RpcResponseStatus(True, port_id, "Traffic stopped") - else: - return RpcResponseStatus(False, port_id, response.data) - - def _handle_get_global_stats_response(self, request, response, success_test): - if response.success: - return CGlobalStats(**response.success) - else: + rc = self.add_stream_pack(stream_list.compiled, port_id_list) + rc.annotate("Attaching streams to port {0}:".format(port_id_list)) + if rc.bad(): return False - def _handle_get_port_stats_response(self, request, response, success_test): - if response.success: - return CPortStats(**response.success) - else: - return False - def _handle_get_stream_stats_response(self, request, response, success_test): - if response.success: - return CStreamStats(**response.success) - else: + # finally, start the traffic + rc = self.start_traffic(mult, port_id_list) + rc.annotate("Starting traffic on ports {0}:".format(port_id_list)) + if rc.bad(): return False + return True - def _process_batch_result(self, req_list, resp_list, handler_func=None, success_test=default_success_test): - res_ok = True - responses = [] - if isinstance(success_test, staticmethod): - success_test = success_test.__func__ - for i, response in enumerate(resp_list): - # run handler method with its params - processed_response = handler_func(req_list[i], response, success_test) - responses.append(processed_response) - if not processed_response.success: - res_ok = False - # else: - # res_ok = False # TODO: mark in this case somehow the bad result - # print res_ok - # print responses - return res_ok, responses - - + # ------ private classes ------ # class CCommLink(object): """describes the connectivity of the stateless client method""" diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index f983d837..252d33bf 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -139,8 +139,8 @@ class CCmdArgParser(argparse.ArgumentParser): opts.ports = self.stateless_client.get_port_ids() for port in opts.ports: - if not self.stateless_client._is_ports_valid(port): - self.error("port id {0} is not a valid\n".format(port)) + if not self.stateless_client.validate_port_list(port): + self.error("port id {0} is not a valid port id\n".format(port)) return opts diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 2be643ab..5ba82dcb 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -296,7 +296,7 @@ class TRexConsole(cmd.Cmd): return - self.stateless_client.cmd_start(opts.ports, stream_list, opts.mult, opts.force, self.annotate) + self.stateless_client.cmd_start(opts.ports, stream_list, opts.mult, opts.force) return @@ -315,7 +315,7 @@ class TRexConsole(cmd.Cmd): if opts is None: return - self.stateless_client.cmd_stop(opts.ports, self.annotate) + self.stateless_client.cmd_stop(opts.ports) return def help_stop(self): @@ -324,7 +324,7 @@ class TRexConsole(cmd.Cmd): ########## reset def do_reset (self, line): '''force stop all ports\n''' - self.stateless_client.cmd_reset(self.annotate) + self.stateless_client.cmd_reset() # tui -- cgit From 57e67fd2ae248039951798978cc8c1c219c3d752 Mon Sep 17 00:00:00 2001 From: imarom Date: Fri, 13 Nov 2015 14:21:55 +0200 Subject: few mods few fixes TUI is not working yet... need to fix more stuff a checkpoint for now --- .../client/trex_stateless_client.py | 47 +++++++++++++++------- .../trex_control_plane/console/parsing_opts.py | 4 +- .../trex_control_plane/console/trex_console.py | 25 ++++-------- 3 files changed, 42 insertions(+), 34 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 93b36f82..011c9426 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -50,15 +50,18 @@ class RC: def err (self): return all([x.data if not x.rc else "" for x in self.rc_list]) - def annotate (self, desc): - print format_text('\n{:<40}'.format(desc), 'bold'), + def annotate (self, desc = None): + if desc: + print format_text('\n{:<40}'.format(desc), 'bold'), if self.bad(): # print all the errors + print "" for x in self.rc_list: if not x.rc: print format_text("\n{0}".format(x.data), 'bold') + print "" print format_text("[FAILED]\n", 'red', 'bold') @@ -274,16 +277,15 @@ class CTRexStatelessClient(object): ############# helper functions section ############## - def validate_port_list(self, port_id): - if isinstance(port_id, list) or isinstance(port_id, set): - # check each item of the sequence - return all([self._is_ports_valid(port) - for port in port_id]) - elif (isinstance(port_id, int)) and (port_id >= 0) and (port_id <= self.get_port_count()): - return True - else: + def validate_port_list(self, port_id_list): + if not isinstance(port_id_list, list): + print type(port_id_list) return False + # check each item of the sequence + return all([ (port_id >= 0) and (port_id < self.get_port_count()) + for port_id in port_id_list ]) + # some preprocessing for port argument def __ports (self, port_id_list): @@ -355,6 +357,8 @@ class CTRexStatelessClient(object): def disconnect(self): self.connected = False self.comm_link.disconnect() + return RC_OK() + ########### cached queries (no server traffic) ########### @@ -384,6 +388,9 @@ class CTRexStatelessClient(object): def get_connection_port (self): return self.comm_link.port + def get_connection_ip (self): + return self.comm_link.server + def get_acquired_ports(self): return [port.port_id for port in self.ports if port.is_acquired()] @@ -543,10 +550,22 @@ class CTRexStatelessClient(object): ######################### Console (high level) API ######################### + def cmd_ping (self): + rc = self.ping() + rc.annotate("Pinging the server on '{0}' port '{1}': ".format(self.get_connection_ip(), self.get_connection_port())) + return rc + + def cmd_connect (self): + rc = self.connect() + rc.annotate() + return rc + + def cmd_disconnect (self): + rc = self.disconnect() + rc.annotate() + return rc + # reset - # acquire, stop, remove streams and clear stats - # - # def cmd_reset (self): @@ -631,7 +650,7 @@ class CTRexStatelessClient(object): return True - + ################################# # ------ private classes ------ # class CCommLink(object): """describes the connectivity of the stateless client method""" diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index 252d33bf..0e11df74 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -139,8 +139,8 @@ class CCmdArgParser(argparse.ArgumentParser): opts.ports = self.stateless_client.get_port_ids() for port in opts.ports: - if not self.stateless_client.validate_port_list(port): - self.error("port id {0} is not a valid port id\n".format(port)) + if not self.stateless_client.validate_port_list([port]): + self.error("port id '{0}' is not a valid port id\n".format(port)) return opts diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 5ba82dcb..bd79479d 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -195,12 +195,8 @@ class TRexConsole(cmd.Cmd): def do_ping (self, line): '''Ping the server\n''' - rc = self.stateless_client.ping() - if rc.good(): - print format_text("[SUCCESS]\n", 'green', 'bold') - else: - print "\n*** " + rc.err() + "\n" - print format_text("[FAILED]\n", 'red', 'bold') + rc = self.stateless_client.cmd_ping() + if rc.bad(): return def do_test (self, line): @@ -230,25 +226,18 @@ class TRexConsole(cmd.Cmd): def do_connect (self, line): '''Connects to the server\n''' - rc = self.stateless_client.connect() - if rc.good(): - print format_text("[SUCCESS]\n", 'green', 'bold') - else: - print "\n*** " + rc.err() + "\n" - print format_text("[FAILED]\n", 'red', 'bold') + rc = self.stateless_client.cmd_connect() + if rc.bad(): return def do_disconnect (self, line): '''Disconnect from the server\n''' - if not self.stateless_client.is_connected(): - print "Not connected to server\n" + rc = self.stateless_client.cmd_disconnect() + if rc.bad(): return - self.stateless_client.disconnect() - print format_text("[SUCCESS]\n", 'green', 'bold') - ############### start @@ -332,7 +321,7 @@ class TRexConsole(cmd.Cmd): '''Shows a graphical console\n''' if not self.stateless_client.is_connected(): - print "Not connected to server\n" + print format_text("\nNot connected to server\n", 'bold') return self.do_verbose('off') -- cgit From 2dee3346a79146b8f042ccb3f105498694fc6c4b Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 15 Nov 2015 14:40:21 +0200 Subject: clean shell / client added script for run --- .../client/trex_stateless_client.py | 246 +++++++++++++++++++-- .../trex_control_plane/console/parsing_opts.py | 9 +- .../trex_control_plane/console/trex_console.py | 150 ++++--------- 3 files changed, 268 insertions(+), 137 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 011c9426..0df2ac5d 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -6,6 +6,7 @@ try: except ImportError: # support import for Python 3 import client.outer_packages + from client_utils.jsonrpc_client import JsonRpcClient, BatchMessage from client_utils.packet_builder import CTRexPktBuilder import json @@ -13,6 +14,8 @@ from common.trex_stats import * from common.trex_streams import * from collections import namedtuple from common.text_opts import * +import parsing_opts +import time from trex_async_client import CTRexAsyncClient @@ -75,6 +78,66 @@ def RC_ERR (err): return RC(False, err) +LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) + +# describes a stream DB +class CStreamsDB(object): + + def __init__(self): + self.stream_packs = {} + + def load_yaml_file (self, filename): + + stream_pack_name = filename + if stream_pack_name in self.get_loaded_streams_names(): + self.remove_stream_packs(stream_pack_name) + + stream_list = CStreamList() + loaded_obj = stream_list.load_yaml(filename) + + try: + compiled_streams = stream_list.compile_streams() + rc = self.load_streams(stream_pack_name, + LoadedStreamList(loaded_obj, + [StreamPack(v.stream_id, v.stream.dump()) + for k, v in compiled_streams.items()])) + + except Exception as e: + return None + + return self.get_stream_pack(stream_pack_name) + + def load_streams(self, name, LoadedStreamList_obj): + if name in self.stream_packs: + return False + else: + self.stream_packs[name] = LoadedStreamList_obj + return True + + def remove_stream_packs(self, *names): + removed_streams = [] + for name in names: + removed = self.stream_packs.pop(name) + if removed: + removed_streams.append(name) + return removed_streams + + def clear(self): + self.stream_packs.clear() + + def get_loaded_streams_names(self): + return self.stream_packs.keys() + + def stream_pack_exists (self, name): + return name in self.get_loaded_streams_names() + + def get_stream_pack(self, name): + if not self.stream_pack_exists(name): + return None + else: + return self.stream_packs.get(name) + + # describes a single port class Port: @@ -96,6 +159,9 @@ class Port: def err (self, msg): return RC_ERR("port {0} : {1}".format(self.port_id, msg)) + def ok (self): + return RC_OK() + # take the port def acquire (self, force = False): params = {"port_id": self.port_id, @@ -106,9 +172,9 @@ class Port: rc = self.transmit(command.method, command.params) if rc.success: self.handler = rc.data - return RC_OK() + return self.ok() else: - return RC_ERR(rc.data) + return self.err(rc.data) # release the port @@ -120,9 +186,9 @@ class Port: rc = self.transmit(command.method, command.params) if rc.success: self.handler = rc.data - return RC_OK() + return self.ok() else: - return RC_ERR(rc.data) + return self.err(rc.data) def is_acquired (self): return (self.handler != None) @@ -147,7 +213,7 @@ class Port: else: raise Exception("port {0}: bad state received from server '{1}'".format(self.port_id, sync_data['state'])) - return RC_OK() + return self.ok() # return TRUE if write commands @@ -178,7 +244,7 @@ class Port: # the only valid state now self.state = self.STATE_STREAMS - return RC_OK() + return self.ok() # remove stream from port def remove_stream (self, stream_id): @@ -197,7 +263,7 @@ class Port: self.streams[stream_id] = None - return RC_OK() + return self.ok() # remove all the streams def remove_all_streams (self): @@ -211,7 +277,7 @@ class Port: self.streams = {} - return RC_OK() + return self.ok() # start traffic def start (self, mul): @@ -234,7 +300,7 @@ class Port: self.state = self.STATE_TX - return RC_OK() + return self.ok() # stop traffic # with force ignores the cached state and sends the command @@ -253,7 +319,7 @@ class Port: # only valid state after stop self.state = self.STATE_STREAMS - return RC_OK() + return self.ok() @@ -273,6 +339,8 @@ class CTRexStatelessClient(object): self._async_client = CTRexAsyncClient(async_port) + self.streams_db = CStreamsDB() + self.connected = False ############# helper functions section ############## @@ -605,15 +673,16 @@ class CTRexStatelessClient(object): active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) if not active_ports: - print format_text("No active traffic on porvided ports", 'bold') - return True + msg = "No active traffic on porvided ports" + print format_text(msg, 'bold') + return RC_ERR(msg) rc = self.stop_traffic(active_ports) rc.annotate("Stopping traffic on port(s) {0}:".format(port_id_list)) if rc.bad(): - return False + return rc - return True + return RC_OK() # start cmd def cmd_start (self, port_id_list, stream_list, mult, force): @@ -622,31 +691,161 @@ class CTRexStatelessClient(object): if active_ports: if not force: - print format_text("Port(s) {0} are active - please stop them or add '--force'".format(active_ports), 'bold') - return False + msg = "Port(s) {0} are active - please stop them or add '--force'".format(active_ports) + print format_text(msg, 'bold') + return RC_ERR(msg) else: rc = self.cmd_stop(active_ports) if not rc: - return False + return rc rc = self.remove_all_streams(port_id_list) - rc.annotate("Removing all streams from ports {0}:".format(port_id_list)) + rc.annotate("Removing all streams from port(s) {0}:".format(port_id_list)) if rc.bad(): - return False + return rc rc = self.add_stream_pack(stream_list.compiled, port_id_list) - rc.annotate("Attaching streams to port {0}:".format(port_id_list)) + rc.annotate("Attaching streams to port(s) {0}:".format(port_id_list)) if rc.bad(): - return False + return rc # finally, start the traffic rc = self.start_traffic(mult, port_id_list) - rc.annotate("Starting traffic on ports {0}:".format(port_id_list)) + rc.annotate("Starting traffic on port(s) {0}:".format(port_id_list)) if rc.bad(): - return False + return rc + + return RC_OK() + + ############## High Level API With Parser ################ + def cmd_start_line (self, line): + '''Start selected traffic in specified ports on TRex\n''' + # define a parser + parser = parsing_opts.gen_parser(self, + "start", + self.cmd_start_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL, + parsing_opts.FORCE, + parsing_opts.STREAM_FROM_PATH_OR_FILE, + parsing_opts.DURATION, + parsing_opts.MULTIPLIER) + + opts = parser.parse_args(line.split()) + + if opts is None: + return RC_ERR("bad command line paramters") + + if opts.db: + stream_list = self.stream_db.get_stream_pack(opts.db) + rc = RC(stream_list != None) + rc.annotate("Load stream pack (from DB):") + if rc.bad(): + return RC_ERR("Failed to load stream pack") + + else: + # load streams from file + stream_list = self.streams_db.load_yaml_file(opts.file[0]) + rc = RC(stream_list != None) + rc.annotate("Load stream pack (from file):") + if stream_list == None: + return RC_ERR("Failed to load stream pack") + + + return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force) + + def cmd_stop_line (self, line): + '''Stop active traffic in specified ports on TRex\n''' + parser = parsing_opts.gen_parser(self, + "stop", + self.cmd_stop_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + + opts = parser.parse_args(line.split()) + if opts is None: + return RC_ERR("bad command line paramters") + + return self.cmd_stop(opts.ports) + + + def cmd_reset_line (self, line): + return self.cmd_reset() + + + def cmd_exit_line (self, line): + print format_text("Exiting\n", 'bold') + # a way to exit + return RC_ERR("exit") + + + def cmd_wait_line (self, line): + '''wait for a period of time\n''' + + parser = parsing_opts.gen_parser(self, + "wait", + self.cmd_wait_line.__doc__, + parsing_opts.DURATION) + + opts = parser.parse_args(line.split()) + if opts is None: + return RC_ERR("bad command line paramters") + + delay_sec = opts.d if opts.d else 1 + + print format_text("Waiting for {0} seconds...\n".format(delay_sec), 'bold') + time.sleep(delay_sec) + + return RC_OK() + + # run a script of commands + def run_script_file (self, filename): + + print format_text("\nRunning script file '{0}'...".format(filename), 'bold') + + rc = self.cmd_connect() + if rc.bad(): + return + + with open(filename) as f: + script_lines = f.readlines() + + cmd_table = {} + + # register all the commands + cmd_table['start'] = self.cmd_start_line + cmd_table['stop'] = self.cmd_stop_line + cmd_table['reset'] = self.cmd_reset_line + cmd_table['wait'] = self.cmd_wait_line + cmd_table['exit'] = self.cmd_exit_line + + for index, line in enumerate(script_lines): + line = line.strip() + if line == "": + continue + if line.startswith("#"): + continue + + sp = line.split(' ', 1) + cmd = sp[0] + if len(sp) == 2: + args = sp[1] + else: + args = "" + + print format_text("Executing line {0} : '{1}'\n".format(index, line)) + + if not cmd in cmd_table: + print "\n*** Error at line {0} : '{1}'\n".format(index, line) + print format_text("unknown command '{0}'\n".format(cmd), 'bold') + return False + + rc = cmd_table[cmd](args) + if rc.bad(): + return False + + print format_text("\n[Done]", 'bold') return True @@ -710,3 +909,4 @@ class CTRexStatelessClient(object): if __name__ == "__main__": pass + diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index 0e11df74..c154ce24 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -135,12 +135,13 @@ class CCmdArgParser(argparse.ArgumentParser): if opts is None: return None - if opts.all_ports: + if getattr(opts, "all_ports", None): opts.ports = self.stateless_client.get_port_ids() - for port in opts.ports: - if not self.stateless_client.validate_port_list([port]): - self.error("port id '{0}' is not a valid port id\n".format(port)) + if getattr(opts, "ports", None): + for port in opts.ports: + if not self.stateless_client.validate_port_list([port]): + self.error("port id '{0}' is not a valid port id\n".format(port)) return opts diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 7931dfc9..88e8dede 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -30,72 +30,12 @@ import tty, termios import trex_root_path from common.trex_streams import * from client.trex_stateless_client import CTRexStatelessClient -from client.trex_stateless_client import RpcResponseStatus from common.text_opts import * from client_utils.general_utils import user_input, get_current_user -import parsing_opts import trex_status -from collections import namedtuple -__version__ = "1.0" - -LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) - -class CStreamsDB(object): - - def __init__(self): - self.stream_packs = {} - - def load_yaml_file (self, filename): - - stream_pack_name = filename - if stream_pack_name in self.get_loaded_streams_names(): - self.remove_stream_packs(stream_pack_name) - - stream_list = CStreamList() - loaded_obj = stream_list.load_yaml(filename) - - try: - compiled_streams = stream_list.compile_streams() - rc = self.load_streams(stream_pack_name, - LoadedStreamList(loaded_obj, - [StreamPack(v.stream_id, v.stream.dump()) - for k, v in compiled_streams.items()])) - - except Exception as e: - return None - - return self.get_stream_pack(stream_pack_name) - - def load_streams(self, name, LoadedStreamList_obj): - if name in self.stream_packs: - return False - else: - self.stream_packs[name] = LoadedStreamList_obj - return True - - def remove_stream_packs(self, *names): - removed_streams = [] - for name in names: - removed = self.stream_packs.pop(name) - if removed: - removed_streams.append(name) - return removed_streams - - def clear(self): - self.stream_packs.clear() - - def get_loaded_streams_names(self): - return self.stream_packs.keys() - def stream_pack_exists (self, name): - return name in self.get_loaded_streams_names() - - def get_stream_pack(self, name): - if not self.stream_pack_exists(name): - return None - else: - return self.stream_packs.get(name) +__version__ = "1.0" # @@ -111,15 +51,11 @@ class TRexConsole(cmd.Cmd): self.verbose = verbose self.acquire_all_ports = acquire_all_ports - self.do_connect("") - self.intro = "\n-=TRex Console v{ver}=-\n".format(ver=__version__) self.intro += "\nType 'help' or '?' for supported actions\n" self.postcmd(False, "") - self.streams_db = CStreamsDB() - ################### internal section ######################## @@ -155,10 +91,21 @@ class TRexConsole(cmd.Cmd): path = dir else: path = "." + + start_string = os.path.basename(text) - return [x - for x in os.listdir(path) - if x.startswith(start_string)] + + targets = [] + + for x in os.listdir(path): + if x.startswith(start_string): + y = os.path.join(path, x) + if os.path.isfile(y): + targets.append(x + ' ') + elif os.path.isdir(y): + targets.append(x + '/') + + return targets # annotation method @staticmethod @@ -256,37 +203,7 @@ class TRexConsole(cmd.Cmd): def do_start(self, line): '''Start selected traffic in specified ports on TRex\n''' - # make sure that the user wants to acquire all - parser = parsing_opts.gen_parser(self.stateless_client, - "start", - self.do_start.__doc__, - parsing_opts.PORT_LIST_WITH_ALL, - parsing_opts.FORCE, - parsing_opts.STREAM_FROM_PATH_OR_FILE, - parsing_opts.DURATION, - parsing_opts.MULTIPLIER) - - opts = parser.parse_args(line.split()) - - if opts is None: - return - - if opts.db: - stream_list = self.stream_db.get_stream_pack(opts.db) - self.annotate("Load stream pack (from DB):", (stream_list != None)) - if stream_list == None: - return - - else: - # load streams from file - stream_list = self.streams_db.load_yaml_file(opts.file[0]) - self.annotate("Load stream pack (from file):", (stream_list != None)) - if stream_list == None: - return - - - self.stateless_client.cmd_start(opts.ports, stream_list, opts.mult, opts.force) - return + self.stateless_client.cmd_start_line(line) def help_start(self): @@ -294,18 +211,9 @@ class TRexConsole(cmd.Cmd): ############# stop def do_stop(self, line): - '''Stop active traffic in specified ports on TRex\n''' - parser = parsing_opts.gen_parser(self.stateless_client, - "stop", - self.do_stop.__doc__, - parsing_opts.PORT_LIST_WITH_ALL) - - opts = parser.parse_args(line.split()) - if opts is None: - return + self.stateless_client.cmd_stop_line(line) - self.stateless_client.cmd_stop(opts.ports) - return + def help_stop(self): self.do_stop("-h") @@ -373,6 +281,14 @@ class TRexConsole(cmd.Cmd): do_exit = do_EOF = do_q = do_quit +# +def is_valid_file(filename): + if not os.path.isfile(filename): + raise argparse.ArgumentTypeError("The file '%s' does not exist" % filename) + + return filename + + def setParserOptions(): parser = argparse.ArgumentParser(prog="trex_console.py") @@ -402,6 +318,12 @@ def setParserOptions(): action="store_false", help="Acquire all ports on connect. Default is: ON.", default = True) + parser.add_argument("--batch", dest="batch", + nargs = 1, + type = is_valid_file, + help = "Run the console in a batch mode with file", + default = None) + return parser @@ -411,7 +333,15 @@ def main(): # Stateless client connection stateless_client = CTRexStatelessClient(options.user, options.server, options.port, options.pub) + rc = stateless_client.cmd_connect() + if rc.bad(): + return + if options.batch: + cont = stateless_client.run_script_file(options.batch[0]) + if not cont: + return + # console try: console = TRexConsole(stateless_client, options.acquire, options.verbose) -- cgit From 56becbc13bc2edc1fe60afb6d788357a70147a43 Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 15 Nov 2015 16:34:16 +0200 Subject: few fixes to ZMQ client (timeout values) --- .../client/trex_stateless_client.py | 4 +- .../client_utils/jsonrpc_client.py | 56 +++++++++++----------- .../trex_control_plane/console/trex_console.py | 1 + 3 files changed, 32 insertions(+), 29 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 0df2ac5d..443466b2 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -419,7 +419,7 @@ class CTRexStatelessClient(object): return RC_OK() def is_connected (self): - return self.connected + return self.connected and self.comm_link.is_connected def disconnect(self): @@ -820,7 +820,7 @@ class CTRexStatelessClient(object): cmd_table['wait'] = self.cmd_wait_line cmd_table['exit'] = self.cmd_exit_line - for index, line in enumerate(script_lines): + for index, line in enumerate(script_lines, start = 1): line = line.strip() if line == "": continue diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index 077c82ad..b826f02f 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -110,45 +110,45 @@ class JsonRpcClient(object): return id, msg - def invoke_rpc_method (self, method_name, params = {}, block = True): + def invoke_rpc_method (self, method_name, params = {}): if not self.connected: return False, "Not connected to server" id, msg = self.create_jsonrpc_v2(method_name, params) - return self.send_raw_msg(msg, block) + return self.send_raw_msg(msg) # low level send of string message - def send_raw_msg (self, msg, block = True): + def send_raw_msg (self, msg): + self.verbose_msg("Sending Request To Server:\n\n" + self.pretty_json(msg) + "\n") - if block: - self.socket.send(msg) - else: + tries = 0 + while True: try: - self.socket.send(msg, flags = zmq.NOBLOCK) - except zmq.error.ZMQError as e: - self.disconnect() - return CmdResponse(False, "Failed To Get Send Message") - - got_response = False + self.socket.send(msg) + break + except zmq.Again: + sleep(0.1) + tries += 1 + if tries > 10: + self.disconnect() + return CmdResponse(False, "Failed to send message to server") + + + tries = 0 + while True: + try: + response = self.socket.recv() + break + except zmq.Again: + sleep(0.1) + tries += 1 + if tries > 10: + self.disconnect() + return CmdResponse(False, "Failed to get server response") - if block: - response = self.socket.recv() - got_response = True - else: - for i in xrange(0 ,10): - try: - response = self.socket.recv(flags = zmq.NOBLOCK) - got_response = True - break - except zmq.Again: - sleep(0.2) - - if not got_response: - self.disconnect() - return CmdResponse(False, "Failed To Get Server Response") self.verbose_msg("Server Response:\n\n" + self.pretty_json(response) + "\n") @@ -223,6 +223,8 @@ class JsonRpcClient(object): except zmq.error.ZMQError as e: return False, "ZMQ Error: Bad server or port name: " + str(e) + self.socket.setsockopt(zmq.SNDTIMEO, 5) + self.socket.setsockopt(zmq.RCVTIMEO, 5) self.connected = True diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 88e8dede..7cb65fa6 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -33,6 +33,7 @@ from client.trex_stateless_client import CTRexStatelessClient from common.text_opts import * from client_utils.general_utils import user_input, get_current_user import trex_status +import parsing_opts __version__ = "1.0" -- cgit From 94b1238942da24e47fb3e689bf695e815a604eb0 Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 15 Nov 2015 18:15:14 +0200 Subject: added duration to the RPC server (and all the way to the DP) *STILL NEEDS FIXING THE DP STOP SCHED MESSAGE" --- .../trex_control_plane/client/trex_stateless_client.py | 15 ++++++++------- .../automation/trex_control_plane/console/parsing_opts.py | 7 +++++-- 2 files changed, 13 insertions(+), 9 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 443466b2..4478ed3f 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -280,7 +280,7 @@ class Port: return self.ok() # start traffic - def start (self, mul): + def start (self, mul, duration): if self.state == self.STATE_DOWN: return self.err("Unable to start traffic - port is down") @@ -292,7 +292,8 @@ class Port: params = {"handler": self.handler, "port_id": self.port_id, - "mul": mul} + "mul": mul, + "duration": duration} rc, data = self.transmit("start_traffic", params) if not rc: @@ -580,14 +581,14 @@ class CTRexStatelessClient(object): return self.ports[port_id].get_stream_id_list() - def start_traffic (self, multiplier, port_id_list = None): + def start_traffic (self, multiplier, duration, port_id_list = None): port_id_list = self.__ports(port_id_list) rc = RC() for port_id in port_id_list: - rc.add(self.ports[port_id].start(multiplier)) + rc.add(self.ports[port_id].start(multiplier, duration)) return rc @@ -685,7 +686,7 @@ class CTRexStatelessClient(object): return RC_OK() # start cmd - def cmd_start (self, port_id_list, stream_list, mult, force): + def cmd_start (self, port_id_list, stream_list, mult, force, duration): active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) @@ -713,7 +714,7 @@ class CTRexStatelessClient(object): # finally, start the traffic - rc = self.start_traffic(mult, port_id_list) + rc = self.start_traffic(mult, duration, port_id_list) rc.annotate("Starting traffic on port(s) {0}:".format(port_id_list)) if rc.bad(): return rc @@ -754,7 +755,7 @@ class CTRexStatelessClient(object): return RC_ERR("Failed to load stream pack") - return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force) + return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force, opts.duration) def cmd_stop_line (self, line): '''Stop active traffic in specified ports on TRex\n''' diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index c154ce24..d5c21af0 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -89,10 +89,13 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], "dest": "all_ports", 'help': "Set this flag to apply the command on all available ports"}), DURATION: ArgumentPack(['-d'], - {"action": "store", + {'action': "store", 'metavar': 'TIME', - "type": match_time_unit, + 'dest': 'duration', + 'type': match_time_unit, + 'default': -1.0, 'help': "Set duration time for TRex."}), + FORCE: ArgumentPack(['--force'], {"action": "store_true", 'default': False, -- cgit From bb6dec2ed238069b6a0c079d2031246704b717c4 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Mon, 16 Nov 2015 21:59:31 +0200 Subject: created general trex console class added dynamic server async support fixed bugs --- .../trex_control_plane/client/trex_async_client.py | 5 +++-- .../trex_control_plane/client/trex_stateless_client.py | 2 +- .../trex_control_plane/console/trex_console.py | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index d13513bf..31bec93f 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -151,16 +151,17 @@ class TrexAsyncStatsManager(): class CTRexAsyncClient(): - def __init__ (self, port): + def __init__ (self, server, port): self.port = port + self.server = server self.raw_snapshot = {} self.stats = TrexAsyncStatsManager() - self.tr = "tcp://localhost:{0}".format(self.port) + self.tr = "tcp://{0}:{1}".format(self.server, self.port) print "\nConnecting To ZMQ Publisher At {0}".format(self.tr) self.active = True diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 4478ed3f..21f62ece 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -338,7 +338,7 @@ class CTRexStatelessClient(object): self._server_version = None self.__err_log = None - self._async_client = CTRexAsyncClient(async_port) + self._async_client = CTRexAsyncClient(server, async_port) self.streams_db = CStreamsDB() diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 7cb65fa6..8e8b25ec 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -39,6 +39,24 @@ import parsing_opts __version__ = "1.0" +class TRexGeneralCmd(cmd.Cmd): + def __init__(self): + cmd.Cmd.__init__(self) + + def emptyline(self): + """Called when an empty line is entered in response to the prompt. + + This overriding is such that when empty line is passed, **nothing happens**. + """ + return + + def completenames(self, text, *ignored): + """ + This overriding is such that a space is added to name completion. + """ + dotext = 'do_'+text + return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)] + # # main console object class TRexConsole(cmd.Cmd): -- cgit From 650549974eb23461ecaf70acf0110a148b0dde70 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 17 Nov 2015 11:22:25 +0200 Subject: minor bug in wait cmd --- scripts/automation/trex_control_plane/client/trex_stateless_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 4478ed3f..db4199c6 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -793,7 +793,7 @@ class CTRexStatelessClient(object): if opts is None: return RC_ERR("bad command line paramters") - delay_sec = opts.d if opts.d else 1 + delay_sec = opts.duration if (opts.duration > 0) else 1 print format_text("Waiting for {0} seconds...\n".format(delay_sec), 'bold') time.sleep(delay_sec) -- cgit From 84e9d7a4a8bbb3afb4861652e2d56bc27097f794 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 19 Nov 2015 02:30:48 +0200 Subject: History feature is DONE Fixed bugs Cleaned code, more like PEP8 --- .../client/trex_stateless_client.py | 55 ++++++++++------------ .../trex_control_plane/console/trex_console.py | 52 ++++++++++++++++---- 2 files changed, 68 insertions(+), 39 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 21f62ece..3dcfae28 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -139,8 +139,7 @@ class CStreamsDB(object): # describes a single port -class Port: - +class Port(object): STATE_DOWN = 0 STATE_IDLE = 1 STATE_STREAMS = 2 @@ -156,14 +155,14 @@ class Port: self.streams = {} - def err (self, msg): + def err(self, msg): return RC_ERR("port {0} : {1}".format(self.port_id, msg)) - def ok (self): + def ok(self): return RC_OK() # take the port - def acquire (self, force = False): + def acquire(self, force = False): params = {"port_id": self.port_id, "user": self.user, "force": force} @@ -178,7 +177,7 @@ class Port: # release the port - def release (self): + def release(self): params = {"port_id": self.port_id, "handler": self.handler} @@ -190,25 +189,24 @@ class Port: else: return self.err(rc.data) - def is_acquired (self): + def is_acquired(self): return (self.handler != None) - def is_active (self): - return (self.state == self.STATE_TX ) or (self.state == self.STATE_PAUSE) - - def sync (self, sync_data): + def is_active(self): + return(self.state == self.STATE_TX ) or (self.state == self.STATE_PAUSE) + def sync(self, sync_data): self.handler = sync_data['handler'] - - if sync_data['state'] == "DOWN": + port_state = sync_data['state'].upper() + if port_state == "DOWN": self.state = self.STATE_DOWN - elif sync_data['state'] == "IDLE": + elif port_state == "IDLE": self.state = self.STATE_IDLE - elif sync_data['state'] == "STREAMS": + elif port_state == "STREAMS": self.state = self.STATE_STREAMS - elif sync_data['state'] == "TX": + elif port_state == "TX": self.state = self.STATE_TX - elif sync_data['state'] == "PAUSE": + elif port_state == "PAUSE": self.state = self.STATE_PAUSE else: raise Exception("port {0}: bad state received from server '{1}'".format(self.port_id, sync_data['state'])) @@ -217,14 +215,14 @@ class Port: # return TRUE if write commands - def is_port_writeable (self): - # operations on port can be done on state idle or state sreams + def is_port_writable (self): + # operations on port can be done on state idle or state streams return ((self.state == self.STATE_IDLE) or (self.state == self.STATE_STREAMS)) # add stream to the port def add_stream (self, stream_id, stream_obj): - if not self.is_port_writeable(): + if not self.is_port_writable(): return self.err("Please stop port before attempting to add streams") @@ -332,6 +330,7 @@ class CTRexStatelessClient(object): self.user = username self.comm_link = CTRexStatelessClient.CCommLink(server, sync_port, virtual) self.verbose = False + self.ports = [] self._conn_handler = {} self._active_ports = set() self._system_info = None @@ -378,7 +377,7 @@ class CTRexStatelessClient(object): ############ boot up section ################ # connection sequence - def connect (self): + def connect(self): self.connected = False @@ -394,7 +393,7 @@ class CTRexStatelessClient(object): self.system_info = data - # cache supported cmds + # cache supported commands rc, data = self.transmit("get_supported_cmds") if not rc: return RC_ERR(data) @@ -402,8 +401,7 @@ class CTRexStatelessClient(object): self.supported_cmds = data # create ports - self.ports = [] - for port_id in xrange(0, self.get_port_count()): + for port_id in xrange(self.get_port_count()): self.ports.append(Port(port_id, self.user, self.transmit)) # acquire all ports @@ -485,7 +483,6 @@ class CTRexStatelessClient(object): return RC_ERR(data) for port_info in data: - rc = self.ports[port_info['port_id']].sync(port_info) if rc.bad(): return rc @@ -619,23 +616,23 @@ class CTRexStatelessClient(object): ######################### Console (high level) API ######################### - def cmd_ping (self): + def cmd_ping(self): rc = self.ping() rc.annotate("Pinging the server on '{0}' port '{1}': ".format(self.get_connection_ip(), self.get_connection_port())) return rc - def cmd_connect (self): + def cmd_connect(self): rc = self.connect() rc.annotate() return rc - def cmd_disconnect (self): + def cmd_disconnect(self): rc = self.disconnect() rc.annotate() return rc # reset - def cmd_reset (self): + def cmd_reset(self): # sync with the server diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 8e8b25ec..ea2f5f12 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -23,6 +23,7 @@ import json import ast import argparse import random +import readline import string import os import sys @@ -36,12 +37,38 @@ import trex_status import parsing_opts -__version__ = "1.0" +__version__ = "1.1" class TRexGeneralCmd(cmd.Cmd): def __init__(self): cmd.Cmd.__init__(self) + # configure history behaviour + self._history_file_dir = "/tmp/trex/console/" + self._history_file = self.get_history_file_full_path() + readline.set_history_length(100) + # load history, if any + self.load_console_history() + + + def get_console_identifier(self): + return self.__class__.__name__ + + def get_history_file_full_path(self): + return "{dir}{filename}.hist".format(dir=self._history_file_dir, + filename=self.get_console_identifier()) + + def load_console_history(self): + if os.path.exists(self._history_file): + readline.read_history_file(self._history_file) + return + + def save_console_history(self): + if not os.path.exists(self._history_file_dir): + os.makedirs(self._history_file_dir) + # os.mknod(self._history_file) + readline.write_history_file(self._history_file) + return def emptyline(self): """Called when an empty line is entered in response to the prompt. @@ -57,15 +84,22 @@ class TRexGeneralCmd(cmd.Cmd): dotext = 'do_'+text return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)] + def precmd(self, line): + # before doing anything, save history snapshot of the console + # this is done before executing the command in case of ungraceful application exit + self.save_console_history() + return line + + # # main console object -class TRexConsole(cmd.Cmd): +class TRexConsole(TRexGeneralCmd): """Trex Console""" - def __init__(self, stateless_client, acquire_all_ports = True, verbose = False): - cmd.Cmd.__init__(self) - + def __init__(self, stateless_client, acquire_all_ports=True, verbose=False): self.stateless_client = stateless_client + TRexGeneralCmd.__init__(self) + self.verbose = verbose self.acquire_all_ports = acquire_all_ports @@ -78,11 +112,9 @@ class TRexConsole(cmd.Cmd): ################### internal section ######################## - # a cool hack - i stole this function and added space - def completenames(self, text, *ignored): - dotext = 'do_'+text - return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)] - + def get_console_identifier(self): + return "{context}_{server}".format(context=self.__class__.__name__, + server=self.stateless_client.get_system_info()['hostname']) def register_main_console_methods(self): main_names = set(self.trex_console.get_names()).difference(set(dir(self.__class__))) -- cgit From 2ae2e4e860194ee8d2b5ec5c4a1375751f51dd98 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 19 Nov 2015 17:17:38 +0200 Subject: full async DP stop support --- .../trex_control_plane/client/trex_async_client.py | 48 ++++++++++++++++------ .../client/trex_stateless_client.py | 9 +++- 2 files changed, 43 insertions(+), 14 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index 31bec93f..adb91d97 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -8,6 +8,8 @@ except ImportError: import client.outer_packages from client_utils.jsonrpc_client import JsonRpcClient, BatchMessage +from common.text_opts import * + import json import threading import time @@ -103,13 +105,9 @@ class TrexAsyncStatsManager(): return self.port_stats[str(port_id)] - def update (self, snapshot): - - if snapshot['name'] == 'trex-global': - self.__handle_snapshot(snapshot['data']) - else: - # for now ignore the rest - return + + def update (self, data): + self.__handle_snapshot(data) def __handle_snapshot (self, snapshot): @@ -151,10 +149,11 @@ class TrexAsyncStatsManager(): class CTRexAsyncClient(): - def __init__ (self, server, port): + def __init__ (self, server, port, stateless_client): self.port = port self.server = server + self.stateless_client = stateless_client self.raw_snapshot = {} @@ -165,14 +164,15 @@ class CTRexAsyncClient(): print "\nConnecting To ZMQ Publisher At {0}".format(self.tr) self.active = True - self.t = threading.Thread(target = self._run) + self.t = threading.Thread(target = self.run) # kill this thread on exit and don't add it to the join list self.t.setDaemon(True) self.t.start() - def _run (self): + + def run (self): # Socket to talk to server self.context = zmq.Context() @@ -185,10 +185,12 @@ class CTRexAsyncClient(): line = self.socket.recv_string(); msg = json.loads(line) - key = msg['name'] - self.raw_snapshot[key] = msg['data'] + name = msg['name'] + data = msg['data'] + type = msg['type'] + self.raw_snapshot[name] = data - self.stats.update(msg) + self.__dispatch(name, type, data) def get_stats (self): @@ -199,6 +201,26 @@ class CTRexAsyncClient(): return self.raw_snapshot + # dispatch the message to the right place + def __dispatch (self, name, type, data): + # stats + if name == "trex-global": + self.stats.update(data) + # events + elif name == "trex-event": + self.__handle_async_event(type, data) + else: + # ignore + pass + + def __handle_async_event (self, type, data): + # DP stopped + if (type == 0): + port_id = int(data['port_id']) + print format_text("\n[Event] - Port {0} Stopped".format(port_id), 'bold') + # call the handler + self.stateless_client.async_event_port_stopped(port_id) + def stop (self): self.active = False self.t.join() diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 699f0af2..dd11fb67 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -320,6 +320,9 @@ class Port(object): return self.ok() + ################# events handler ###################### + def async_event_port_stopped (self): + self.state = self.STATE_STREAMS class CTRexStatelessClient(object): @@ -337,12 +340,16 @@ class CTRexStatelessClient(object): self._server_version = None self.__err_log = None - self._async_client = CTRexAsyncClient(server, async_port) + self._async_client = CTRexAsyncClient(server, async_port, self) self.streams_db = CStreamsDB() self.connected = False + ################# events handler ###################### + def async_event_port_stopped (self, port_id): + self.ports[port_id].async_event_port_stopped() + ############# helper functions section ############## def validate_port_list(self, port_id_list): -- cgit From 903b855393acd411e85b25e6b2df1158d9fe2856 Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 22 Nov 2015 11:07:40 +0200 Subject: TUI back online --- .../trex_control_plane/client/trex_stateless_client.py | 18 ++++++++++++++++++ .../trex_control_plane/console/trex_console.py | 5 ++--- .../trex_control_plane/console/trex_status.py | 16 ++++------------ 3 files changed, 24 insertions(+), 15 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index dd11fb67..164cdb90 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -277,6 +277,17 @@ class Port(object): return self.ok() + # get a specific stream + def get_stream (self, stream_id): + if stream_id in self.streams: + return self.streams[stream_id] + else: + return None + + def get_all_streams (self): + return self.streams + + # start traffic def start (self, mul, duration): if self.state == self.STATE_DOWN: @@ -393,6 +404,13 @@ class CTRexStatelessClient(object): if not rc: return RC_ERR(data) + # version + rc, data = self.transmit("get_version") + if not rc: + return RC_ERR(data) + + self.server_version = data + # cache system info rc, data = self.transmit("get_system_info") if not rc: diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index ea2f5f12..995965fd 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -197,8 +197,6 @@ class TRexConsole(TRexGeneralCmd): if rc.bad(): return - def do_test (self, line): - print self.stateless_client.get_acquired_ports() # set verbose on / off def do_verbose(self, line): @@ -252,7 +250,7 @@ class TRexConsole(TRexGeneralCmd): return TRexConsole.tree_autocomplete(s[l - 1]) def do_start(self, line): - '''Start selected traffic in specified ports on TRex\n''' + '''Start selected traffic in specified port(s) on TRex\n''' self.stateless_client.cmd_start_line(line) @@ -262,6 +260,7 @@ class TRexConsole(TRexGeneralCmd): ############# stop def do_stop(self, line): + '''stops port(s) transmitting traffic\n''' self.stateless_client.cmd_stop_line(line) diff --git a/scripts/automation/trex_control_plane/console/trex_status.py b/scripts/automation/trex_control_plane/console/trex_status.py index a54b718e..869812a1 100644 --- a/scripts/automation/trex_control_plane/console/trex_status.py +++ b/scripts/automation/trex_control_plane/console/trex_status.py @@ -398,13 +398,9 @@ class TrexStatus(): self.general_stats = stateless_client.get_stats_async().get_general_stats() # fetch server info - rc, self.server_sys_info = self.stateless_client.get_system_info() - if not rc: - return + self.server_sys_info = self.stateless_client.get_system_info() - rc, self.server_version = self.stateless_client.get_version() - if not rc: - return + self.server_version = self.stateless_client.get_version() # list of owned ports self.owned_ports_list = self.stateless_client.get_acquired_ports() @@ -416,9 +412,7 @@ class TrexStatus(): self.owned_ports[str(port_id)] = {} self.owned_ports[str(port_id)]['streams'] = {} - rc, stream_list = self.stateless_client.get_all_streams(port_id) - if not rc: - raise Exception("unable to get streams") + stream_list = self.stateless_client.get_all_streams(port_id) self.owned_ports[str(port_id)] = stream_list @@ -481,9 +475,7 @@ class TrexStatus(): self.owned_ports[str(port_id)] = {} self.owned_ports[str(port_id)]['streams'] = {} - rc, stream_list = self.stateless_client.get_all_streams(port_id) - if not rc: - raise Exception("unable to get streams") + stream_list = self.stateless_client.get_all_streams(port_id) self.owned_ports[str(port_id)] = stream_list -- cgit From 54c1f0fc29b3d4580f7a13cffbe625fe88e37b16 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Mon, 23 Nov 2015 14:10:19 +0200 Subject: add pause/resume into the console --- .../client/trex_stateless_client.py | 118 +++++++++++++++++++++ .../trex_control_plane/console/old_console.py | 12 +++ .../trex_control_plane/console/trex_console.py | 10 ++ 3 files changed, 140 insertions(+) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 164cdb90..7bcbf2c7 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -331,6 +331,40 @@ class Port(object): return self.ok() + def pause (self): + + if (self.state != self.STATE_TX) : + return self.err("port is not transmitting") + + params = {"handler": self.handler, + "port_id": self.port_id} + + rc, data = self.transmit("pause_traffic", params) + if not rc: + return self.err(data) + + # only valid state after stop + self.state = self.STATE_PAUSE + + return self.ok() + + def resume (self): + + if (self.state != self.STATE_PAUSE) : + return self.err("port is not in pause mode") + + params = {"handler": self.handler, + "port_id": self.port_id} + + rc, data = self.transmit("resume_traffic", params) + if not rc: + return self.err(data) + + # only valid state after stop + self.state = self.STATE_TX + + return self.ok() + ################# events handler ###################### def async_event_port_stopped (self): self.state = self.STATE_STREAMS @@ -615,6 +649,25 @@ class CTRexStatelessClient(object): return rc + def resume_traffic (self, port_id_list = None, force = False): + + port_id_list = self.__ports(port_id_list) + rc = RC() + + for port_id in port_id_list: + rc.add(self.ports[port_id].resume()) + + return rc + + def pause_traffic (self, port_id_list = None, force = False): + + port_id_list = self.__ports(port_id_list) + rc = RC() + + for port_id in port_id_list: + rc.add(self.ports[port_id].pause()) + + return rc def stop_traffic (self, port_id_list = None, force = False): @@ -707,6 +760,71 @@ class CTRexStatelessClient(object): return RC_OK() + # pause cmd + def cmd_pause (self, port_id_list): + + # find the relveant ports + active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) + + if not active_ports: + msg = "No active traffic on porvided ports" + print format_text(msg, 'bold') + return RC_ERR(msg) + + rc = self.pause_traffic(active_ports) + rc.annotate("Pausing traffic on port(s) {0}:".format(port_id_list)) + if rc.bad(): + return rc + + return RC_OK() + + def cmd_pause_line (self, line): + '''Pause active traffic in specified ports on TRex\n''' + parser = parsing_opts.gen_parser(self, + "pause", + self.cmd_stop_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + + opts = parser.parse_args(line.split()) + if opts is None: + return RC_ERR("bad command line paramters") + + return self.cmd_pause(opts.ports) + + + # resume cmd + def cmd_resume (self, port_id_list): + + # find the relveant ports + active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) + + if not active_ports: + msg = "No active traffic on porvided ports" + print format_text(msg, 'bold') + return RC_ERR(msg) + + rc = self.resume_traffic(active_ports) + rc.annotate("Resume traffic on port(s) {0}:".format(port_id_list)) + if rc.bad(): + return rc + + return RC_OK() + + + def cmd_resume_line (self, line): + '''Resume active traffic in specified ports on TRex\n''' + parser = parsing_opts.gen_parser(self, + "resume", + self.cmd_stop_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + + opts = parser.parse_args(line.split()) + if opts is None: + return RC_ERR("bad command line paramters") + + return self.cmd_resume(opts.ports) + + # start cmd def cmd_start (self, port_id_list, stream_list, mult, force, duration): diff --git a/scripts/automation/trex_control_plane/console/old_console.py b/scripts/automation/trex_control_plane/console/old_console.py index 93c7e3f4..9d61a3a6 100644 --- a/scripts/automation/trex_control_plane/console/old_console.py +++ b/scripts/automation/trex_control_plane/console/old_console.py @@ -636,6 +636,18 @@ class TRexConsole1(cmd.Cmd): res_ok = self.stop_traffic(port_list) return + def do_pause(self, line): + '''Pause active traffic in specified ports on TRex\n''' + parser = parsing_opts.gen_parser("stop", self.do_stop.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + opts = parser.parse_args(line.split()) + if opts is None: + # avoid further processing in this command + return + port_list = self.extract_port_list(opts) + res_ok = self.stop_traffic(port_list) + return + def help_stop(self): self.do_stop("-h") diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 995965fd..c03f2a82 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -263,6 +263,16 @@ class TRexConsole(TRexGeneralCmd): '''stops port(s) transmitting traffic\n''' self.stateless_client.cmd_stop_line(line) + ############# stop + def do_pause(self, line): + '''pause port(s) transmitting traffic\n''' + self.stateless_client.cmd_pause_line(line) + + ############# stop + def do_resume(self, line): + '''resume port(s) transmitting traffic\n''' + self.stateless_client.cmd_resume_line(line) + def help_stop(self): -- cgit From 1f6977d1e109acba69f1bf2230d6b9f5e4aae54e Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 23 Nov 2015 18:02:14 +0200 Subject: add stream graph + support for console to use any of the following bps, kbps, mbps, gbps, pps, kpps, mbps percentage is not working yet --- .../trex_control_plane/console/parsing_opts.py | 64 +++++++++++++++++----- 1 file changed, 51 insertions(+), 13 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index d5c21af0..732ba764 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -44,22 +44,60 @@ def match_time_unit(val): def match_multiplier(val): '''match some val against multiplier shortcut inputs ''' - match = re.match("^(\d+)(gb|kpps|%?)$", val) + + match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mbps|%?)$", val) + + result = {} + if match: - digit = int(match.group(1)) - unit = match.group(2) + + value = float(match.group(1)) + unit = match.group(3) + + # raw type (factor) if not unit: - return digit - elif unit == 'gb': - raise NotImplementedError("gb units are not supported yet") - else: - raise NotImplementedError("kpps units are not supported yet") + result['type'] = 'raw' + result['factor'] = value + + elif unit == 'bps': + result['type'] = 'max_bps' + result['max'] = value + + elif unit == 'kbps': + result['type'] = 'max_bps' + result['max'] = value * 1000 + + elif unit == 'mbps': + result['type'] = 'max_bps' + result['max'] = value * 1000 * 1000 + + elif unit == 'gbps': + result['type'] = 'max_bps' + result['max'] = value * 1000 * 1000 * 1000 + + elif unit == 'pps': + result['type'] = 'max_pps' + result['max'] = value + + elif unit == "kpps": + result['type'] = 'max_pps' + result['max'] = value * 1000 + + elif unit == "mpps": + result['type'] = 'max_pps' + result['max'] = value * 1000 * 1000 + + elif unit == "%": + raise argparse.ArgumentTypeError("percetange is currently unsupported...") + + return result + else: - raise argparse.ArgumentTypeError("Multiplier should be passed in the following format: \n" - "-m 100 : multiply stream file by this factor \n" - "-m 10gb : from graph calculate the maximum rate as this bandwidth (for each port)\n" - "-m 10kpps : from graph calculate the maximum rate as this pps (for each port)\n" - "-m 40% : from graph calculate the maximum rate as this percent from total port (for each port)") + raise argparse.ArgumentTypeError("\n\nMultiplier should be passed in the following format: \n\n" + "-m 100 : multiply by factor \n" + "-m 1bps / 1kbps / 1mbps / 1gbps : normalize to bps \n" + "-m 1pps / 1kpps / 1mbps : normalize to pps \n" + "-m 40% : normalize to % from port line rate\n") -- cgit From 1e1c11059b7f7fcb5c160dffe2df832a8adf719d Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Tue, 24 Nov 2015 11:41:19 +0200 Subject: minor console fix --- scripts/automation/trex_control_plane/console/trex_console.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index c03f2a82..2a6fd2eb 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -263,12 +263,12 @@ class TRexConsole(TRexGeneralCmd): '''stops port(s) transmitting traffic\n''' self.stateless_client.cmd_stop_line(line) - ############# stop + ############# pause def do_pause(self, line): '''pause port(s) transmitting traffic\n''' self.stateless_client.cmd_pause_line(line) - ############# stop + ############# resume def do_resume(self, line): '''resume port(s) transmitting traffic\n''' self.stateless_client.cmd_resume_line(line) -- cgit From 045d0f8f31b2e68b8f726377fabb3205b5d19cf5 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 24 Nov 2015 18:40:16 +0200 Subject: added support for percentage on start also added info about the correct interface driver and speed from DPDK layer --- .../trex_control_plane/client/trex_stateless_client.py | 17 ++++++++++++++--- .../trex_control_plane/console/parsing_opts.py | 18 +++++++++++------- 2 files changed, 25 insertions(+), 10 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 7bcbf2c7..af166b7f 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -146,13 +146,14 @@ class Port(object): STATE_TX = 3 STATE_PAUSE = 4 - def __init__ (self, port_id, user, transmit): + def __init__ (self, port_id, speed, driver, user, transmit): self.port_id = port_id self.state = self.STATE_IDLE self.handler = None self.transmit = transmit self.user = user - + self.driver = driver + self.speed = speed self.streams = {} def err(self, msg): @@ -161,6 +162,9 @@ class Port(object): def ok(self): return RC_OK() + def get_speed_bps (self): + return (self.speed * 1000 * 1000) + # take the port def acquire(self, force = False): params = {"port_id": self.port_id, @@ -299,6 +303,11 @@ class Port(object): if self.state == self.STATE_TX: return self.err("Unable to start traffic - port is already transmitting") + # if percentage - translate + if mul['type'] == 'percentage': + mul['type'] = 'max_bps' + mul['max'] = self.get_speed_bps() * (mul['max'] / 100) + params = {"handler": self.handler, "port_id": self.port_id, "mul": mul, @@ -461,7 +470,9 @@ class CTRexStatelessClient(object): # create ports for port_id in xrange(self.get_port_count()): - self.ports.append(Port(port_id, self.user, self.transmit)) + speed = self.system_info['ports'][port_id]['speed'] + driver = self.system_info['ports'][port_id]['driver'] + self.ports.append(Port(port_id, speed, driver, self.user, self.transmit)) # acquire all ports rc = self.acquire() diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index 732ba764..ab678586 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -42,6 +42,12 @@ def match_time_unit(val): "-d 10m : in min \n" "-d 1h : in hours") +match_multiplier_help = """Multiplier should be passed in the following format: + [number][ | bps | kbps | mbps | gbps | pps | kpps | mpps | %% ]. + no suffix will provide an absoulute factor and percentage + will provide a percentage of the line rate. examples + : '-m 10', '-m 10kbps', '-m 10mpps', '-m 23%%' """ + def match_multiplier(val): '''match some val against multiplier shortcut inputs ''' @@ -88,16 +94,14 @@ def match_multiplier(val): result['max'] = value * 1000 * 1000 elif unit == "%": - raise argparse.ArgumentTypeError("percetange is currently unsupported...") + # will be translated by the port object + result['type'] = 'percentage' + result['max'] = value return result else: - raise argparse.ArgumentTypeError("\n\nMultiplier should be passed in the following format: \n\n" - "-m 100 : multiply by factor \n" - "-m 1bps / 1kbps / 1mbps / 1gbps : normalize to bps \n" - "-m 1pps / 1kpps / 1mbps : normalize to pps \n" - "-m 40% : normalize to % from port line rate\n") + raise argparse.ArgumentTypeError(match_multiplier_help) @@ -109,7 +113,7 @@ def is_valid_file(filename): OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], - {'help': "Set multiplier for stream", + {'help': match_multiplier_help, 'dest': "mult", 'default': 1.0, 'type': match_multiplier}), -- cgit From 963da09589fe8376f2750e4d0dc741d48eea6132 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 25 Nov 2015 03:51:37 -0500 Subject: added history command support - you can show the history or execute a previous command --- .../trex_control_plane/console/parsing_opts.py | 5 ++ .../trex_control_plane/console/trex_console.py | 57 +++++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index ab678586..14e8c563 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -23,6 +23,11 @@ FORCE = 10 # list of ArgumentGroup types MUTEX = 1 +def check_negative(value): + ivalue = int(value) + if ivalue < 0: + raise argparse.ArgumentTypeError("non positive value provided: '{0}'".format(value)) + return ivalue def match_time_unit(val): '''match some val against time shortcut inputs ''' diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 2a6fd2eb..0dabe2c3 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -70,6 +70,23 @@ class TRexGeneralCmd(cmd.Cmd): readline.write_history_file(self._history_file) return + def print_history (self): + + length = readline.get_current_history_length() + + for i in xrange(1, length + 1): + cmd = readline.get_history_item(i) + print "{:<5} {:}".format(i, cmd) + + def get_history_item (self, index): + length = readline.get_current_history_length() + if index > length: + print format_text("please select an index between {0} and {1}".format(0, length)) + return None + + return readline.get_history_item(index) + + def emptyline(self): """Called when an empty line is entered in response to the prompt. @@ -217,6 +234,41 @@ class TRexConsole(TRexGeneralCmd): else: print format_text("\nplease specify 'on' or 'off'\n", 'bold') + # show history + def help_history (self): + self.do_history("-h") + + def do_history (self, line): + '''Manage the command history\n''' + + item = parsing_opts.ArgumentPack(['item'], + {"nargs": '?', + 'metavar': 'item', + 'type': parsing_opts.check_negative, + 'help': "an history item index", + 'default': 0}) + + parser = parsing_opts.gen_parser(self, + "history", + self.do_history.__doc__, + item) + + opts = parser.parse_args(line.split()) + if opts is None: + return + + if opts.item == 0: + self.print_history() + else: + cmd = self.get_history_item(opts.item) + print cmd + if cmd == None: + return + + print cmd + self.onecmd(cmd) + + ############### connect def do_connect (self, line): @@ -324,7 +376,7 @@ class TRexConsole(TRexGeneralCmd): cmds = [x[3:] for x in self.get_names() if x.startswith("do_")] for cmd in cmds: - if ( (cmd == "EOF") or (cmd == "q") or (cmd == "exit")): + if ( (cmd == "EOF") or (cmd == "q") or (cmd == "exit") or (cmd == "h")): continue try: @@ -338,8 +390,9 @@ class TRexConsole(TRexGeneralCmd): print "{:<30} {:<30}".format(cmd + " - ", help) + # aliases do_exit = do_EOF = do_q = do_quit - + do_h = do_history # def is_valid_file(filename): -- cgit From b0446de90c05d0a129fc38e6aef981dff243ccb7 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 25 Nov 2015 04:11:29 -0500 Subject: removed some garbage prints --- scripts/automation/trex_control_plane/console/trex_console.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 0dabe2c3..dc1515e0 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -261,11 +261,9 @@ class TRexConsole(TRexGeneralCmd): self.print_history() else: cmd = self.get_history_item(opts.item) - print cmd if cmd == None: return - print cmd self.onecmd(cmd) -- cgit From 4a41c26a5cf23635a5b1332af6e806c915cf7e6c Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 25 Nov 2015 04:46:56 -0500 Subject: support for "total" flag for start also fixed a bug in mpps (trex console) --- .../trex_control_plane/client/trex_stateless_client.py | 5 +++++ .../automation/trex_control_plane/console/parsing_opts.py | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index af166b7f..30550ca3 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -880,6 +880,7 @@ class CTRexStatelessClient(object): "start", self.cmd_start_line.__doc__, parsing_opts.PORT_LIST_WITH_ALL, + parsing_opts.TOTAL, parsing_opts.FORCE, parsing_opts.STREAM_FROM_PATH_OR_FILE, parsing_opts.DURATION, @@ -906,6 +907,10 @@ class CTRexStatelessClient(object): return RC_ERR("Failed to load stream pack") + if opts.total: + # if total was set - divide it between the ports + opts.mult['max'] = opts.mult['max'] / len(opts.ports) + return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force, opts.duration) def cmd_stop_line (self, line): diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index 14e8c563..d2484a83 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -19,6 +19,7 @@ SERVER_IP = 7 STREAM_FROM_PATH_OR_FILE = 8 DURATION = 9 FORCE = 10 +TOTAL = 11 # list of ArgumentGroup types MUTEX = 1 @@ -56,7 +57,7 @@ match_multiplier_help = """Multiplier should be passed in the following format: def match_multiplier(val): '''match some val against multiplier shortcut inputs ''' - match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mbps|%?)$", val) + match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)$", val) result = {} @@ -68,7 +69,7 @@ def match_multiplier(val): # raw type (factor) if not unit: result['type'] = 'raw' - result['factor'] = value + result['max'] = value elif unit == 'bps': result['type'] = 'max_bps' @@ -123,6 +124,13 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'default': 1.0, 'type': match_multiplier}), + + TOTAL: ArgumentPack(['-t', '--total'], + {'help': "traffic will be divided between all ports specified", + 'dest': "total", + 'default': False, + 'action': "store_true"}), + PORT_LIST: ArgumentPack(['--port'], {"nargs": '+', 'dest':'ports', -- cgit From 92dea3787ab7b2c9877af9cd539d42a6957f7da6 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 25 Nov 2015 06:56:04 -0500 Subject: added events log --- .../trex_control_plane/client/trex_async_client.py | 11 +------ .../client/trex_stateless_client.py | 35 ++++++++++++++++++++++ .../trex_control_plane/console/trex_console.py | 6 ++++ 3 files changed, 42 insertions(+), 10 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index adb91d97..7641a1e3 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -197,7 +197,6 @@ class CTRexAsyncClient(): return self.stats def get_raw_snapshot (self): - #return str(self.stats.global_stats.get('m_total_tx_bytes')) + " / " + str(self.stats.global_stats.get_rel('m_total_tx_bytes')) return self.raw_snapshot @@ -208,18 +207,10 @@ class CTRexAsyncClient(): self.stats.update(data) # events elif name == "trex-event": - self.__handle_async_event(type, data) + self.stateless_client.handle_async_event(type, data) else: - # ignore pass - def __handle_async_event (self, type, data): - # DP stopped - if (type == 0): - port_id = int(data['port_id']) - print format_text("\n[Event] - Port {0} Stopped".format(port_id), 'bold') - # call the handler - self.stateless_client.async_event_port_stopped(port_id) def stop (self): self.active = False diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 30550ca3..1d0ca606 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -16,6 +16,7 @@ from collections import namedtuple from common.text_opts import * import parsing_opts import time +import datetime from trex_async_client import CTRexAsyncClient @@ -400,10 +401,44 @@ class CTRexStatelessClient(object): self.connected = False + self.events = [] + ################# events handler ###################### + + def handle_async_event (self, type, data): + # DP stopped + + ev = "[event] - " + + if (type == 0): + port_id = int(data['port_id']) + ev += "Port {0} has stopped".format(port_id) + # call the handler + self.async_event_port_stopped(port_id) + + elif (type == 1): + ev += "Server has stopped" + self.async_event_server_stopped() + + else: + # unknown event - ignore + return + + print format_text("\n" + ev, 'bold') + + ts = time.time() + st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') + self.events.append("{0} - ".format(st) + format_text(ev, 'bold')) + def async_event_port_stopped (self, port_id): self.ports[port_id].async_event_port_stopped() + def async_event_server_stopped (self): + self.disconnect() + + def get_events (self): + return self.events + ############# helper functions section ############## def validate_port_list(self, port_id_list): diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index dc1515e0..b164af4e 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -334,6 +334,12 @@ class TRexConsole(TRexGeneralCmd): self.stateless_client.cmd_reset() + def do_events (self, line): + '''shows events recieved from server\n''' + events = self.stateless_client.get_events() + for ev in events: + print ev + # tui def do_tui (self, line): '''Shows a graphical console\n''' -- cgit From b6ec2066653319b60385de1d4117165eb88890a1 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 25 Nov 2015 07:39:39 -0500 Subject: fixed a bug with default start command in the console also added clear events and more types of events --- .../client/trex_stateless_client.py | 30 ++++++++++++++++++++-- .../trex_control_plane/console/parsing_opts.py | 2 +- .../trex_control_plane/console/trex_console.py | 22 ++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 1d0ca606..149d2855 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -410,26 +410,49 @@ class CTRexStatelessClient(object): ev = "[event] - " + show_event = False + + # port started if (type == 0): + port_id = int(data['port_id']) + ev += "Port {0} has started".format(port_id) + + # port stopped + elif (type == 1): port_id = int(data['port_id']) ev += "Port {0} has stopped".format(port_id) + # call the handler self.async_event_port_stopped(port_id) + - elif (type == 1): + # server stopped + elif (type == 2): ev += "Server has stopped" self.async_event_server_stopped() + show_event = True + + # port finished traffic + elif (type == 3): + port_id = int(data['port_id']) + ev += "Port {0} job done".format(port_id) + + # call the handler + self.async_event_port_stopped(port_id) + show_event = True else: # unknown event - ignore return - print format_text("\n" + ev, 'bold') + if show_event: + print format_text("\n" + ev, 'bold') ts = time.time() st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') self.events.append("{0} - ".format(st) + format_text(ev, 'bold')) + def async_event_port_stopped (self, port_id): self.ports[port_id].async_event_port_stopped() @@ -439,6 +462,9 @@ class CTRexStatelessClient(object): def get_events (self): return self.events + def clear_events (self): + self.events = [] + ############# helper functions section ############## def validate_port_list(self, port_id_list): diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index d2484a83..0bcdce84 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -121,7 +121,7 @@ def is_valid_file(filename): OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'help': match_multiplier_help, 'dest': "mult", - 'default': 1.0, + 'default': {'type':'raw', 'max':1}, 'type': match_multiplier}), diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index b164af4e..fc2c845a 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -334,12 +334,34 @@ class TRexConsole(TRexGeneralCmd): self.stateless_client.cmd_reset() + def help_events (self): + self.do_events("-h") + 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") + # tui def do_tui (self, line): '''Shows a graphical console\n''' -- cgit From d9a11302236095e055247295021bdfce6c988802 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 26 Nov 2015 02:30:03 -0500 Subject: added support for 'update' --- .../client/trex_stateless_client.py | 88 ++++++++++++++++++++-- .../trex_control_plane/console/trex_console.py | 16 +++- 2 files changed, 93 insertions(+), 11 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 149d2855..c5b28f5b 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -164,7 +164,7 @@ class Port(object): return RC_OK() def get_speed_bps (self): - return (self.speed * 1000 * 1000) + return (self.speed * 1000 * 1000 * 1000) # take the port def acquire(self, force = False): @@ -293,6 +293,13 @@ class Port(object): return self.streams + def process_mul (self, mul): + # if percentage - translate + if mul['type'] == 'percentage': + mul['type'] = 'max_bps' + mul['max'] = self.get_speed_bps() * (mul['max'] / 100) + + # start traffic def start (self, mul, duration): if self.state == self.STATE_DOWN: @@ -304,11 +311,8 @@ class Port(object): if self.state == self.STATE_TX: return self.err("Unable to start traffic - port is already transmitting") - # if percentage - translate - if mul['type'] == 'percentage': - mul['type'] = 'max_bps' - mul['max'] = self.get_speed_bps() * (mul['max'] / 100) - + self.process_mul(mul) + params = {"handler": self.handler, "port_id": self.port_id, "mul": mul, @@ -358,6 +362,7 @@ class Port(object): return self.ok() + def resume (self): if (self.state != self.STATE_PAUSE) : @@ -375,6 +380,23 @@ class Port(object): return self.ok() + + def update (self, mul): + if (self.state != self.STATE_TX) : + return self.err("port is not transmitting") + + self.process_mul(mul) + + params = {"handler": self.handler, + "port_id": self.port_id, + "mul": mul} + + rc, data = self.transmit("update_traffic", params) + if not rc: + return self.err(data) + + return self.ok() + ################# events handler ###################### def async_event_port_stopped (self): self.state = self.STATE_STREAMS @@ -752,6 +774,17 @@ class CTRexStatelessClient(object): return rc + def update_traffic (self, mult, port_id_list = None, force = False): + + port_id_list = self.__ports(port_id_list) + rc = RC() + + for port_id in port_id_list: + rc.add(self.ports[port_id].update(mult)) + + return rc + + def get_port_stats(self, port_id=None): pass @@ -832,6 +865,25 @@ class CTRexStatelessClient(object): return RC_OK() + # update cmd + def cmd_update (self, port_id_list, mult): + + # find the relveant ports + active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) + + if not active_ports: + msg = "No active traffic on porvided ports" + print format_text(msg, 'bold') + return RC_ERR(msg) + + rc = self.update_traffic(mult, active_ports) + rc.annotate("Updating traffic on port(s) {0}:".format(port_id_list)) + if rc.bad(): + return rc + + return RC_OK() + + # pause cmd def cmd_pause (self, port_id_list): @@ -968,7 +1020,8 @@ class CTRexStatelessClient(object): return RC_ERR("Failed to load stream pack") - if opts.total: + # total has no meaning with percentage - its linear + if opts.total and (mult['type'] != 'percentage'): # if total was set - divide it between the ports opts.mult['max'] = opts.mult['max'] / len(opts.ports) @@ -988,6 +1041,27 @@ class CTRexStatelessClient(object): return self.cmd_stop(opts.ports) + def cmd_update_line (self, line): + '''Update port(s) speed currently active\n''' + parser = parsing_opts.gen_parser(self, + "update", + self.cmd_update_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL, + parsing_opts.MULTIPLIER, + parsing_opts.TOTAL) + + opts = parser.parse_args(line.split()) + if opts is None: + return RC_ERR("bad command line paramters") + + # total has no meaning with percentage - its linear + if opts.total and (opts.mult['type'] != 'percentage'): + # if total was set - divide it between the ports + opts.mult['max'] = opts.mult['max'] / len(opts.ports) + + return self.cmd_update(opts.ports, opts.mult) + + def cmd_reset_line (self, line): return self.cmd_reset() diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index fc2c845a..9e44daac 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -313,6 +313,17 @@ class TRexConsole(TRexGeneralCmd): '''stops port(s) transmitting traffic\n''' self.stateless_client.cmd_stop_line(line) + def help_stop(self): + self.do_stop("-h") + + ############# update + def do_update(self, line): + '''update speed of port(s)currently transmitting traffic\n''' + self.stateless_client.cmd_update_line(line) + + def help_update (self): + self.do_update("-h") + ############# pause def do_pause(self, line): '''pause port(s) transmitting traffic\n''' @@ -323,10 +334,7 @@ class TRexConsole(TRexGeneralCmd): '''resume port(s) transmitting traffic\n''' self.stateless_client.cmd_resume_line(line) - - - def help_stop(self): - self.do_stop("-h") + ########## reset def do_reset (self, line): -- cgit From 91f6c24f45cbb0cbf8568a9938059a1a934e6ae6 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 26 Nov 2015 13:06:36 +0200 Subject: Initial implementation of stats prompting --- .../trex_control_plane/client/trex_async_client.py | 48 ++--- .../client/trex_stateless_client.py | 108 +++++++++-- .../client_utils/external_packages.py | 3 +- .../client_utils/jsonrpc_client.py | 4 +- .../client_utils/parsing_opts.py | 213 +++++++++++++++++++++ .../trex_control_plane/client_utils/text_tables.py | 34 ++++ .../trex_control_plane/common/trex_stats.py | 80 ++++++++ .../trex_control_plane/console/parsing_opts.py | 193 ------------------- .../trex_control_plane/console/trex_console.py | 11 +- .../trex_control_plane/console/trex_status.py | 4 +- 10 files changed, 462 insertions(+), 236 deletions(-) create mode 100755 scripts/automation/trex_control_plane/client_utils/parsing_opts.py create mode 100644 scripts/automation/trex_control_plane/client_utils/text_tables.py delete mode 100755 scripts/automation/trex_control_plane/console/parsing_opts.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index adb91d97..12c89c1a 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -21,13 +21,14 @@ from common.trex_stats import * from common.trex_streams import * # basic async stats class -class TrexAsyncStats(object): +class CTRexAsyncStats(object): def __init__ (self): self.ref_point = None self.current = {} self.last_update_ts = datetime.datetime.now() - def __format_num (self, size, suffix = ""): + @staticmethod + def format_num (size, suffix = ""): for unit in ['','K','M','G','T','P']: if abs(size) < 1000.0: @@ -47,7 +48,7 @@ class TrexAsyncStats(object): self.ref_point = self.current - def get (self, field, format = False, suffix = ""): + def get(self, field, format = False, suffix = ""): if not field in self.current: return "N/A" @@ -55,7 +56,7 @@ class TrexAsyncStats(object): if not format: return self.current[field] else: - return self.__format_num(self.current[field], suffix) + return self.format_num(self.current[field], suffix) def get_rel (self, field, format = False, suffix = ""): @@ -65,7 +66,7 @@ class TrexAsyncStats(object): if not format: return (self.current[field] - self.ref_point[field]) else: - return self.__format_num(self.current[field] - self.ref_point[field], suffix) + return self.format_num(self.current[field] - self.ref_point[field], suffix) # return true if new data has arrived in the past 2 seconds @@ -74,28 +75,28 @@ class TrexAsyncStats(object): return (delta_ms < 2000) # describes the general stats provided by TRex -class TrexAsyncStatsGeneral(TrexAsyncStats): +class CTRexAsyncStatsGeneral(CTRexAsyncStats): def __init__ (self): - super(TrexAsyncStatsGeneral, self).__init__() + super(CTRexAsyncStatsGeneral, self).__init__() # per port stats -class TrexAsyncStatsPort(TrexAsyncStats): +class CTRexAsyncStatsPort(CTRexAsyncStats): def __init__ (self): - super(TrexAsyncStatsPort, self).__init__() + super(CTRexAsyncStatsPort, self).__init__() def get_stream_stats (self, stream_id): return None # stats manager -class TrexAsyncStatsManager(): +class CTRexAsyncStatsManager(): def __init__ (self): - self.general_stats = TrexAsyncStatsGeneral() + self.general_stats = CTRexAsyncStatsGeneral() self.port_stats = {} - def get_general_stats (self): + def get_general_stats(self): return self.general_stats def get_port_stats (self, port_id): @@ -106,10 +107,10 @@ class TrexAsyncStatsManager(): return self.port_stats[str(port_id)] - def update (self, data): + def update(self, data): self.__handle_snapshot(data) - def __handle_snapshot (self, snapshot): + def __handle_snapshot(self, snapshot): general_stats = {} port_stats = {} @@ -140,7 +141,7 @@ class TrexAsyncStatsManager(): for port_id, data in port_stats.iteritems(): if not port_id in self.port_stats: - self.port_stats[port_id] = TrexAsyncStatsPort() + self.port_stats[port_id] = CTRexAsyncStatsPort() self.port_stats[port_id].update(data) @@ -157,22 +158,20 @@ class CTRexAsyncClient(): self.raw_snapshot = {} - self.stats = TrexAsyncStatsManager() + self.stats = CTRexAsyncStatsManager() self.tr = "tcp://{0}:{1}".format(self.server, self.port) print "\nConnecting To ZMQ Publisher At {0}".format(self.tr) self.active = True - self.t = threading.Thread(target = self.run) + self.t = threading.Thread(target= self.run) # kill this thread on exit and don't add it to the join list self.t.setDaemon(True) self.t.start() - - - def run (self): + def run(self): # Socket to talk to server self.context = zmq.Context() @@ -182,7 +181,7 @@ class CTRexAsyncClient(): self.socket.setsockopt(zmq.SUBSCRIBE, '') while self.active: - line = self.socket.recv_string(); + line = self.socket.recv_string() msg = json.loads(line) name = msg['name'] @@ -192,15 +191,13 @@ class CTRexAsyncClient(): self.__dispatch(name, type, data) - - def get_stats (self): + def get_stats(self): return self.stats def get_raw_snapshot (self): #return str(self.stats.global_stats.get('m_total_tx_bytes')) + " / " + str(self.stats.global_stats.get_rel('m_total_tx_bytes')) return self.raw_snapshot - # dispatch the message to the right place def __dispatch (self, name, type, data): # stats @@ -225,3 +222,6 @@ class CTRexAsyncClient(): self.active = False self.t.join() + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 7bcbf2c7..4cb70483 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -10,11 +10,13 @@ except ImportError: from client_utils.jsonrpc_client import JsonRpcClient, BatchMessage from client_utils.packet_builder import CTRexPktBuilder import json -from common.trex_stats import * + from common.trex_streams import * from collections import namedtuple from common.text_opts import * -import parsing_opts +# import trex_stats +from common import trex_stats +from client_utils import parsing_opts, text_tables import time from trex_async_client import CTRexAsyncClient @@ -29,7 +31,7 @@ class RpcResponseStatus(namedtuple('RpcResponseStatus', ['success', 'id', 'msg'] stat="success" if self.success else "fail") # simple class to represent complex return value -class RC: +class RC(): def __init__ (self, rc = None, data = None): self.rc_list = [] @@ -74,7 +76,7 @@ class RC: def RC_OK(): return RC(True, "") -def RC_ERR (err): +def RC_ERR(err): return RC(False, err) @@ -86,7 +88,7 @@ class CStreamsDB(object): def __init__(self): self.stream_packs = {} - def load_yaml_file (self, filename): + def load_yaml_file(self, filename): stream_pack_name = filename if stream_pack_name in self.get_loaded_streams_names(): @@ -376,6 +378,7 @@ class CTRexStatelessClient(object): def __init__(self, username, server="localhost", sync_port = 5050, async_port = 4500, virtual=False): super(CTRexStatelessClient, self).__init__() self.user = username + self.system_info = None self.comm_link = CTRexStatelessClient.CCommLink(server, sync_port, virtual) self.verbose = False self.ports = [] @@ -388,6 +391,11 @@ class CTRexStatelessClient(object): self._async_client = CTRexAsyncClient(server, async_port, self) self.streams_db = CStreamsDB() + self.info_and_stats = trex_stats.CTRexInformationCenter({"server": server, + "sync_port": sync_port, + "async_port": async_port}, + self.ports, + self.get_stats_async()) self.connected = False @@ -444,13 +452,15 @@ class CTRexStatelessClient(object): return RC_ERR(data) self.server_version = data + self.info_and_stats.server_version = data # cache system info + # self.get_system_info(refresh=True) rc, data = self.transmit("get_system_info") if not rc: return RC_ERR(data) - self.system_info = data + self.info_and_stats.system_info = data # cache supported commands rc, data = self.transmit("get_supported_cmds") @@ -508,7 +518,7 @@ class CTRexStatelessClient(object): else: return port_ids - def get_stats_async (self): + def get_stats_async(self): return self._async_client.get_stats() def get_connection_port (self): @@ -548,6 +558,9 @@ class CTRexStatelessClient(object): return RC_OK() + def get_global_stats(self): + rc, info = self.transmit("get_global_stats") + return RC(rc, info) ########## port commands ############## @@ -787,7 +800,7 @@ class CTRexStatelessClient(object): opts = parser.parse_args(line.split()) if opts is None: - return RC_ERR("bad command line paramters") + return RC_ERR("bad command line parameters") return self.cmd_pause(opts.ports) @@ -820,7 +833,7 @@ class CTRexStatelessClient(object): opts = parser.parse_args(line.split()) if opts is None: - return RC_ERR("bad command line paramters") + return RC_ERR("bad command line parameters") return self.cmd_resume(opts.ports) @@ -861,6 +874,18 @@ class CTRexStatelessClient(object): return RC_OK() + def cmd_stats(self, port_id_list, stats_mask=set()): + print port_id_list + print stats_mask + stats_opts = trex_stats.ALL_STATS_OPTS.intersection(stats_mask) + print stats_opts + + stats_obj = {} + for stats_type in stats_opts: + stats_obj.update(self.info_and_stats.generate_single_statistic(stats_type)) + return stats_obj + pass + ############## High Level API With Parser ################ def cmd_start_line (self, line): '''Start selected traffic in specified ports on TRex\n''' @@ -877,10 +902,10 @@ class CTRexStatelessClient(object): opts = parser.parse_args(line.split()) if opts is None: - return RC_ERR("bad command line paramters") + return RC_ERR("bad command line parameters") if opts.db: - stream_list = self.stream_db.get_stream_pack(opts.db) + stream_list = self.streams_db.get_stream_pack(opts.db) rc = RC(stream_list != None) rc.annotate("Load stream pack (from DB):") if rc.bad(): @@ -906,7 +931,7 @@ class CTRexStatelessClient(object): opts = parser.parse_args(line.split()) if opts is None: - return RC_ERR("bad command line paramters") + return RC_ERR("bad command line parameters") return self.cmd_stop(opts.ports) @@ -915,6 +940,49 @@ class CTRexStatelessClient(object): return self.cmd_reset() + def cmd_stats_line (self, line): + '''Fetch statistics from TRex server by port\n''' + # define a parser + parser = parsing_opts.gen_parser(self, + "stats", + self.cmd_stats_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL, + parsing_opts.STATS_MASK) + + opts = parser.parse_args(line.split()) + + if opts is None: + return RC_ERR("bad command line parameters") + + print opts + print self.get_global_stats() + # determine stats mask + mask = self._get_mask_keys(**self._filter_namespace_args(opts, ['p', 'g', 'ps'])) + # get stats objects, as dictionary + stats = self.cmd_stats(opts.ports, mask) + # print stats to screen + for stat_type, stat_data in stats.iteritems(): + text_tables.print_table_with_header(stat_data.text_table, stat_type) + return + + # if opts.db: + # stream_list = self.streams_db.get_stream_pack(opts.db) + # rc = RC(stream_list != None) + # rc.annotate("Load stream pack (from DB):") + # if rc.bad(): + # return RC_ERR("Failed to load stream pack") + # + # else: + # # load streams from file + # stream_list = self.streams_db.load_yaml_file(opts.file[0]) + # rc = RC(stream_list != None) + # rc.annotate("Load stream pack (from file):") + # if stream_list == None: + # return RC_ERR("Failed to load stream pack") + # + # + # return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force, opts.duration) + def cmd_exit_line (self, line): print format_text("Exiting\n", 'bold') # a way to exit @@ -931,7 +999,7 @@ class CTRexStatelessClient(object): opts = parser.parse_args(line.split()) if opts is None: - return RC_ERR("bad command line paramters") + return RC_ERR("bad command line parameters") delay_sec = opts.duration if (opts.duration > 0) else 1 @@ -990,6 +1058,20 @@ class CTRexStatelessClient(object): return True + ################################# + # ------ private methods ------ # + @staticmethod + def _get_mask_keys(ok_values={True}, **kwargs): + masked_keys = set() + for key, val in kwargs.iteritems(): + if val in ok_values: + masked_keys.add(key) + return masked_keys + + @staticmethod + def _filter_namespace_args(namespace, ok_values): + return {k: v for k, v in namespace.__dict__.items() if k in ok_values} + ################################# # ------ private classes ------ # class CCommLink(object): diff --git a/scripts/automation/trex_control_plane/client_utils/external_packages.py b/scripts/automation/trex_control_plane/client_utils/external_packages.py index e2bb37a5..3c6eb449 100755 --- a/scripts/automation/trex_control_plane/client_utils/external_packages.py +++ b/scripts/automation/trex_control_plane/client_utils/external_packages.py @@ -9,7 +9,8 @@ PATH_TO_PYTHON_LIB = os.path.abspath(os.path.join(ROOT_PATH, os.pardir, os.pard CLIENT_UTILS_MODULES = ['zmq', 'dpkt-1.8.6', - 'PyYAML-3.01/lib' + 'PyYAML-3.01/lib', + 'texttable-0.8.4' ] def import_client_utils_modules(): diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index b826f02f..dd208da4 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -174,7 +174,7 @@ class JsonRpcClient(object): def process_single_response (self, response_json): if (response_json.get("jsonrpc") != "2.0"): - return False, "Malfromed Response ({0})".format(str(response)) + return False, "Malformed Response ({0})".format(str(response_json)) # error reported by server if ("error" in response_json): @@ -185,7 +185,7 @@ class JsonRpcClient(object): # if no error there should be a result if ("result" not in response_json): - return False, "Malformed Response ({0})".format(str(response)) + return False, "Malformed Response ({0})".format(str(response_json)) return True, response_json["result"] diff --git a/scripts/automation/trex_control_plane/client_utils/parsing_opts.py b/scripts/automation/trex_control_plane/client_utils/parsing_opts.py new file mode 100755 index 00000000..c110983b --- /dev/null +++ b/scripts/automation/trex_control_plane/client_utils/parsing_opts.py @@ -0,0 +1,213 @@ +import argparse +from collections import namedtuple +import sys +import re +import os + +ArgumentPack = namedtuple('ArgumentPack', ['name_or_flags', 'options']) +ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options']) + + +# list of available parsing options +MULTIPLIER = 1 +PORT_LIST = 2 +ALL_PORTS = 3 +PORT_LIST_WITH_ALL = 4 +FILE_PATH = 5 +FILE_FROM_DB = 6 +SERVER_IP = 7 +STREAM_FROM_PATH_OR_FILE = 8 +DURATION = 9 +FORCE = 10 +GLOBAL_STATS = 11 +PORT_STATS = 12 +PORT_STATUS = 13 +STATS_MASK = 14 + +# list of ArgumentGroup types +MUTEX = 1 + + +def match_time_unit(val): + '''match some val against time shortcut inputs ''' + match = re.match("^(\d+)([m|h]?)$", val) + if match: + digit = int(match.group(1)) + unit = match.group(2) + if not unit: + return digit + elif unit == 'm': + return digit*60 + else: + return digit*60*60 + else: + raise argparse.ArgumentTypeError("Duration should be passed in the following format: \n" + "-d 100 : in sec \n" + "-d 10m : in min \n" + "-d 1h : in hours") + +def match_multiplier(val): + '''match some val against multiplier shortcut inputs ''' + match = re.match("^(\d+)(gb|kpps|%?)$", val) + if match: + digit = int(match.group(1)) + unit = match.group(2) + if not unit: + return digit + elif unit == 'gb': + raise NotImplementedError("gb units are not supported yet") + else: + raise NotImplementedError("kpps units are not supported yet") + else: + raise argparse.ArgumentTypeError("Multiplier should be passed in the following format: \n" + "-m 100 : multiply stream file by this factor \n" + "-m 10gb : from graph calculate the maximum rate as this bandwidth (for each port)\n" + "-m 10kpps : from graph calculate the maximum rate as this pps (for each port)\n" + "-m 40% : from graph calculate the maximum rate as this percent from total port (for each port)") + + + +def is_valid_file(filename): + if not os.path.isfile(filename): + raise argparse.ArgumentTypeError("The file '%s' does not exist" % filename) + + return filename + + +OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], + {'help': "Set multiplier for stream", + 'dest': "mult", + 'default': 1.0, + 'type': match_multiplier}), + + PORT_LIST: ArgumentPack(['--port'], + {"nargs": '+', + 'dest':'ports', + 'metavar': 'PORTS', + 'type': int, + 'help': "A list of ports on which to apply the command", + 'default': []}), + + ALL_PORTS: ArgumentPack(['-a'], + {"action": "store_true", + "dest": "all_ports", + 'help': "Set this flag to apply the command on all available ports"}), + DURATION: ArgumentPack(['-d'], + {'action': "store", + 'metavar': 'TIME', + 'dest': 'duration', + 'type': match_time_unit, + 'default': -1.0, + 'help': "Set duration time for TRex."}), + + FORCE: ArgumentPack(['--force'], + {"action": "store_true", + 'default': False, + 'help': "Set if you want to stop active ports before applying new TRex run on them."}), + + FILE_PATH: ArgumentPack(['-f'], + {'metavar': 'FILE', + 'dest': 'file', + 'nargs': 1, + 'type': is_valid_file, + 'help': "File path to YAML file that describes a stream pack. "}), + + FILE_FROM_DB: ArgumentPack(['--db'], + {'metavar': 'LOADED_STREAM_PACK', + 'help': "A stream pack which already loaded into console cache."}), + + SERVER_IP: ArgumentPack(['--server'], + {'metavar': 'SERVER', + 'help': "server IP"}), + + GLOBAL_STATS: ArgumentPack(['-g'], + {'action': 'store_true', + 'help': "Fetch only global statistics"}), + + PORT_STATS: ArgumentPack(['-p'], + {'action': 'store_true', + 'help': "Fetch only port statistics"}), + + PORT_STATUS: ArgumentPack(['--ps'], + {'action': 'store_true', + 'help': "Fetch only port status data"}), + + # advanced options + PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST, + ALL_PORTS], + {'required': True}), + STREAM_FROM_PATH_OR_FILE: ArgumentGroup(MUTEX, [FILE_PATH, + FILE_FROM_DB], + {'required': True}), + STATS_MASK: ArgumentGroup(MUTEX, [GLOBAL_STATS, + PORT_STATS, + PORT_STATUS], + {}) + } + + +class CCmdArgParser(argparse.ArgumentParser): + + def __init__(self, stateless_client, *args, **kwargs): + super(CCmdArgParser, self).__init__(*args, **kwargs) + self.stateless_client = stateless_client + + def parse_args(self, args=None, namespace=None): + try: + opts = super(CCmdArgParser, self).parse_args(args, namespace) + if opts is None: + return None + + if getattr(opts, "all_ports", None): + opts.ports = self.stateless_client.get_port_ids() + + if getattr(opts, "ports", None): + for port in opts.ports: + if not self.stateless_client.validate_port_list([port]): + self.error("port id '{0}' is not a valid port id\n".format(port)) + + return opts + + except SystemExit: + # recover from system exit scenarios, such as "help", or bad arguments. + return None + + +def get_flags (opt): + return OPTIONS_DB[opt].name_or_flags + +def gen_parser(stateless_client, op_name, description, *args): + parser = CCmdArgParser(stateless_client, prog=op_name, conflict_handler='resolve', + description=description) + for param in args: + try: + + if isinstance(param, int): + argument = OPTIONS_DB[param] + else: + argument = param + + if isinstance(argument, ArgumentGroup): + if argument.type == MUTEX: + # handle as mutually exclusive group + group = parser.add_mutually_exclusive_group(**argument.options) + for sub_argument in argument.args: + group.add_argument(*OPTIONS_DB[sub_argument].name_or_flags, + **OPTIONS_DB[sub_argument].options) + else: + # ignore invalid objects + continue + elif isinstance(argument, ArgumentPack): + parser.add_argument(*argument.name_or_flags, + **argument.options) + else: + # ignore invalid objects + continue + except KeyError as e: + cause = e.args[0] + raise KeyError("The attribute '{0}' is missing as a field of the {1} option.\n".format(cause, param)) + return parser + + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/client_utils/text_tables.py b/scripts/automation/trex_control_plane/client_utils/text_tables.py new file mode 100644 index 00000000..2debca38 --- /dev/null +++ b/scripts/automation/trex_control_plane/client_utils/text_tables.py @@ -0,0 +1,34 @@ + +import external_packages +from texttable import Texttable +from common.text_opts import format_text + +class TRexTextTable(Texttable): + + def __init__(self): + Texttable.__init__(self) + # set class attributes so that it'll be more like TRex standard output + self.set_chars(['-', '|', '-', '-']) + self.set_deco(Texttable.HEADER | Texttable.VLINES) + +class TRexTextInfo(Texttable): + + def __init__(self): + Texttable.__init__(self) + # set class attributes so that it'll be more like TRex standard output + self.set_chars(['-', ':', '-', '-']) + self.set_deco(Texttable.VLINES) + +def generate_trex_stats_table(): + pass + +def print_table_with_header(texttable_obj, header=""): + header = header.replace("_", " ").title() + print format_text(header, 'cyan', 'underline') + "\n" + print texttable_obj.draw() + "\n" + + pass + +if __name__ == "__main__": + pass + diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py index b7e768c1..bf5ba2bb 100755 --- a/scripts/automation/trex_control_plane/common/trex_stats.py +++ b/scripts/automation/trex_control_plane/common/trex_stats.py @@ -1,6 +1,86 @@ #!/router/bin/python +from collections import namedtuple, OrderedDict +from client_utils import text_tables import copy +GLOBAL_STATS = 'g' +PORT_STATS = 'p' +PORT_STATUS = 'ps' +ALL_STATS_OPTS = {GLOBAL_STATS, PORT_STATS, PORT_STATUS} +ExportableStats = namedtuple('ExportableStats', ['raw_data', 'text_table']) + + +class CTRexInformationCenter(object): + + def __init__(self, connection_info, ports_ref, async_stats_ref): + self.connection_info = connection_info + self.server_version = None + self.system_info = None + self._ports = ports_ref + self._async_stats = async_stats_ref + + # def __getitem__(self, item): + # stats_obj = getattr(self, item) + # if stats_obj: + # return stats_obj.get_stats() + # else: + # return None + + def generate_single_statistic(self, statistic_type): + if statistic_type == GLOBAL_STATS: + return self._generate_global_stats() + elif statistic_type == PORT_STATS: + # return generate_global_stats() + pass + elif statistic_type == PORT_STATUS: + pass + else: + # ignore by returning empty object + return {} + + def _generate_global_stats(self): + stats_obj = self._async_stats.get_general_stats() + return_stats_data = \ + OrderedDict([("connection", "{host}, Port {port}".format(host=self.connection_info.get("server"), + port=self.connection_info.get("sync_port"))), + ("version", self.server_version.get("version", "N/A")), + ("cpu_util", stats_obj.get("m_cpu_util")), + ("total_tx", stats_obj.get("m_tx_bps", format=True, suffix="b/sec")), + # {'m_tx_bps': stats_obj.get("m_tx_bps", format= True, suffix= "b/sec"), + # 'm_tx_pps': stats_obj.get("m_tx_pps", format= True, suffix= "pkt/sec"), + # 'm_total_tx_bytes':stats_obj.get_rel("m_total_tx_bytes", + # format= True, + # suffix = "B"), + # 'm_total_tx_pkts': stats_obj.get_rel("m_total_tx_pkts", + # format= True, + # suffix = "pkts")}, + ("total_rx", stats_obj.get("m_rx_bps", format=True, suffix="b/sec")), + # {'m_rx_bps': stats_obj.get("m_rx_bps", format= True, suffix= "b/sec"), + # 'm_rx_pps': stats_obj.get("m_rx_pps", format= True, suffix= "pkt/sec"), + # 'm_total_rx_bytes': stats_obj.get_rel("m_total_rx_bytes", + # format= True, + # suffix = "B"), + # 'm_total_rx_pkts': stats_obj.get_rel("m_total_rx_pkts", + # format= True, + # suffix = "pkts")}, + ("total_pps", stats_obj.format_num(stats_obj.get("m_tx_pps") + stats_obj.get("m_rx_pps"), + suffix="pkt/sec")), + ("total_streams", sum([len(port.streams) + for port in self._ports])), + ("active_ports", sum([port.is_active() + for port in self._ports])) + ] + ) + + # build table representation + stats_table = text_tables.TRexTextInfo() + stats_table.set_cols_align(["l", "l"]) + stats_table.add_rows([[k.replace("_", " ").title(), v] + for k, v in return_stats_data.iteritems()], + header=False) + + return {"global_statistics": ExportableStats(return_stats_data, stats_table)} + class CTRexStatsManager(object): diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py deleted file mode 100755 index d5c21af0..00000000 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ /dev/null @@ -1,193 +0,0 @@ -import argparse -from collections import namedtuple -import sys -import re -import os - -ArgumentPack = namedtuple('ArgumentPack', ['name_or_flags', 'options']) -ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options']) - - -# list of available parsing options -MULTIPLIER = 1 -PORT_LIST = 2 -ALL_PORTS = 3 -PORT_LIST_WITH_ALL = 4 -FILE_PATH = 5 -FILE_FROM_DB = 6 -SERVER_IP = 7 -STREAM_FROM_PATH_OR_FILE = 8 -DURATION = 9 -FORCE = 10 - -# list of ArgumentGroup types -MUTEX = 1 - - -def match_time_unit(val): - '''match some val against time shortcut inputs ''' - match = re.match("^(\d+)([m|h]?)$", val) - if match: - digit = int(match.group(1)) - unit = match.group(2) - if not unit: - return digit - elif unit == 'm': - return digit*60 - else: - return digit*60*60 - else: - raise argparse.ArgumentTypeError("Duration should be passed in the following format: \n" - "-d 100 : in sec \n" - "-d 10m : in min \n" - "-d 1h : in hours") - -def match_multiplier(val): - '''match some val against multiplier shortcut inputs ''' - match = re.match("^(\d+)(gb|kpps|%?)$", val) - if match: - digit = int(match.group(1)) - unit = match.group(2) - if not unit: - return digit - elif unit == 'gb': - raise NotImplementedError("gb units are not supported yet") - else: - raise NotImplementedError("kpps units are not supported yet") - else: - raise argparse.ArgumentTypeError("Multiplier should be passed in the following format: \n" - "-m 100 : multiply stream file by this factor \n" - "-m 10gb : from graph calculate the maximum rate as this bandwidth (for each port)\n" - "-m 10kpps : from graph calculate the maximum rate as this pps (for each port)\n" - "-m 40% : from graph calculate the maximum rate as this percent from total port (for each port)") - - - -def is_valid_file(filename): - if not os.path.isfile(filename): - raise argparse.ArgumentTypeError("The file '%s' does not exist" % filename) - - return filename - - -OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], - {'help': "Set multiplier for stream", - 'dest': "mult", - 'default': 1.0, - 'type': match_multiplier}), - - PORT_LIST: ArgumentPack(['--port'], - {"nargs": '+', - 'dest':'ports', - 'metavar': 'PORTS', - 'type': int, - 'help': "A list of ports on which to apply the command", - 'default': []}), - - ALL_PORTS: ArgumentPack(['-a'], - {"action": "store_true", - "dest": "all_ports", - 'help': "Set this flag to apply the command on all available ports"}), - DURATION: ArgumentPack(['-d'], - {'action': "store", - 'metavar': 'TIME', - 'dest': 'duration', - 'type': match_time_unit, - 'default': -1.0, - 'help': "Set duration time for TRex."}), - - FORCE: ArgumentPack(['--force'], - {"action": "store_true", - 'default': False, - 'help': "Set if you want to stop active ports before applying new TRex run on them."}), - - FILE_PATH: ArgumentPack(['-f'], - {'metavar': 'FILE', - 'dest': 'file', - 'nargs': 1, - 'type': is_valid_file, - 'help': "File path to YAML file that describes a stream pack. "}), - - FILE_FROM_DB: ArgumentPack(['--db'], - {'metavar': 'LOADED_STREAM_PACK', - 'help': "A stream pack which already loaded into console cache."}), - - SERVER_IP: ArgumentPack(['--server'], - {'metavar': 'SERVER', - 'help': "server IP"}), - - # advanced options - PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST, - ALL_PORTS], - {'required': True}), - STREAM_FROM_PATH_OR_FILE: ArgumentGroup(MUTEX, [FILE_PATH, - FILE_FROM_DB], - {'required': True}) - } - - -class CCmdArgParser(argparse.ArgumentParser): - - def __init__(self, stateless_client, *args, **kwargs): - super(CCmdArgParser, self).__init__(*args, **kwargs) - self.stateless_client = stateless_client - - def parse_args(self, args=None, namespace=None): - try: - opts = super(CCmdArgParser, self).parse_args(args, namespace) - if opts is None: - return None - - if getattr(opts, "all_ports", None): - opts.ports = self.stateless_client.get_port_ids() - - if getattr(opts, "ports", None): - for port in opts.ports: - if not self.stateless_client.validate_port_list([port]): - self.error("port id '{0}' is not a valid port id\n".format(port)) - - return opts - - except SystemExit: - # recover from system exit scenarios, such as "help", or bad arguments. - return None - - -def get_flags (opt): - return OPTIONS_DB[opt].name_or_flags - -def gen_parser(stateless_client, op_name, description, *args): - parser = CCmdArgParser(stateless_client, prog=op_name, conflict_handler='resolve', - description=description) - for param in args: - try: - - if isinstance(param, int): - argument = OPTIONS_DB[param] - else: - argument = param - - if isinstance(argument, ArgumentGroup): - if argument.type == MUTEX: - # handle as mutually exclusive group - group = parser.add_mutually_exclusive_group(**argument.options) - for sub_argument in argument.args: - group.add_argument(*OPTIONS_DB[sub_argument].name_or_flags, - **OPTIONS_DB[sub_argument].options) - else: - # ignore invalid objects - continue - elif isinstance(argument, ArgumentPack): - parser.add_argument(*argument.name_or_flags, - **argument.options) - else: - # ignore invalid objects - continue - except KeyError as e: - cause = e.args[0] - raise KeyError("The attribute '{0}' is missing as a field of the {1} option.\n".format(cause, param)) - return parser - - -if __name__ == "__main__": - pass \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index c03f2a82..7d4f3c27 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -33,8 +33,9 @@ from common.trex_streams import * from client.trex_stateless_client import CTRexStatelessClient from common.text_opts import * from client_utils.general_utils import user_input, get_current_user +from client_utils import parsing_opts import trex_status -import parsing_opts + __version__ = "1.1" @@ -283,6 +284,14 @@ class TRexConsole(TRexGeneralCmd): '''force stop all ports\n''' self.stateless_client.cmd_reset() + def do_stats(self, line): + '''Fetch statistics from TRex server by port\n''' + self.stateless_client.cmd_stats_line(line) + pass + + def help_stats(self): + self.do_stats("-h") + # tui def do_tui (self, line): diff --git a/scripts/automation/trex_control_plane/console/trex_status.py b/scripts/automation/trex_control_plane/console/trex_status.py index 869812a1..10ac75c9 100644 --- a/scripts/automation/trex_control_plane/console/trex_status.py +++ b/scripts/automation/trex_control_plane/console/trex_status.py @@ -385,7 +385,7 @@ class TrexStatusCommands(): # # # -class TrexStatus(): +class CTRexStatus(): def __init__ (self, stdscr, stateless_client): self.stdscr = stdscr @@ -506,7 +506,7 @@ def show_trex_status_internal (stdscr, stateless_client): global trex_status if trex_status == None: - trex_status = TrexStatus(stdscr, stateless_client) + trex_status = CTRexStatus(stdscr, stateless_client) trex_status.run() -- cgit From 27a7103d501e9a0bf005d657cb3f7c51a72eca6b Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 26 Nov 2015 09:00:58 -0500 Subject: when connection is lost - identify this on the console --- .../trex_control_plane/client/trex_async_client.py | 11 ++++- .../client/trex_stateless_client.py | 48 ++++++++++++++++------ .../trex_control_plane/console/trex_console.py | 33 +++++++++++++++ 3 files changed, 78 insertions(+), 14 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index 7641a1e3..6793a4ca 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -180,9 +180,18 @@ class CTRexAsyncClient(): self.socket.connect(self.tr) self.socket.setsockopt(zmq.SUBSCRIBE, '') + self.socket.setsockopt(zmq.RCVTIMEO, 3000) while self.active: - line = self.socket.recv_string(); + try: + + line = self.socket.recv_string(); + self.stateless_client.on_async_alive() + + except zmq.Again: + self.stateless_client.on_async_dead() + continue + msg = json.loads(line) name = msg['name'] diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index c5b28f5b..b3476268 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -425,24 +425,36 @@ class CTRexStatelessClient(object): self.events = [] + ################# events handler ###################### - + 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'))) + + if show: + print format_text("\n{:^8} - {:}".format(prefix, format_text(msg, 'bold'))) + def handle_async_event (self, type, data): # DP stopped - ev = "[event] - " - show_event = False # port started if (type == 0): port_id = int(data['port_id']) - ev += "Port {0} has started".format(port_id) + ev = "Port {0} has started".format(port_id) # port stopped elif (type == 1): port_id = int(data['port_id']) - ev += "Port {0} has stopped".format(port_id) + ev = "Port {0} has stopped".format(port_id) # call the handler self.async_event_port_stopped(port_id) @@ -450,14 +462,14 @@ class CTRexStatelessClient(object): # server stopped elif (type == 2): - ev += "Server has stopped" + ev = "Server has stopped" self.async_event_server_stopped() show_event = True # port finished traffic elif (type == 3): port_id = int(data['port_id']) - ev += "Port {0} job done".format(port_id) + ev = "Port {0} job done".format(port_id) # call the handler self.async_event_port_stopped(port_id) @@ -467,12 +479,8 @@ class CTRexStatelessClient(object): # unknown event - ignore return - if show_event: - print format_text("\n" + ev, 'bold') - ts = time.time() - st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') - self.events.append("{0} - ".format(st) + format_text(ev, 'bold')) + self.add_event_log(ev, 'server', show_event) def async_event_port_stopped (self, port_id): @@ -580,6 +588,19 @@ class CTRexStatelessClient(object): return RC_OK() + def on_async_dead (self): + if self.is_connected(): + msg = 'lost connection to server' + self.add_event_log(msg, 'local', True) + + self.disconnect() + + def on_async_alive (self): + if not self.is_connected(): + msg = 'server connection restored' + self.add_event_log(msg, 'local', True) + + self.cmd_connect() ########### cached queries (no server traffic) ########### @@ -1021,7 +1042,7 @@ class CTRexStatelessClient(object): # total has no meaning with percentage - its linear - if opts.total and (mult['type'] != 'percentage'): + if opts.total and (opts.mult['type'] != 'percentage'): # if total was set - divide it between the ports opts.mult['max'] = opts.mult['max'] / len(opts.ports) @@ -1202,3 +1223,4 @@ class CTRexStatelessClient(object): if __name__ == "__main__": pass + \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 9e44daac..e537c306 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -210,6 +210,10 @@ class TRexConsole(TRexGeneralCmd): def do_ping (self, line): '''Ping the server\n''' + if not self.stateless_client.is_connected(): + print format_text("\nNot connected to server\n", 'bold') + return + rc = self.stateless_client.cmd_ping() if rc.bad(): return @@ -302,6 +306,10 @@ class TRexConsole(TRexGeneralCmd): def do_start(self, line): '''Start selected traffic in specified port(s) on TRex\n''' + if not self.stateless_client.is_connected(): + print format_text("\nNot connected to server\n", 'bold') + return + self.stateless_client.cmd_start_line(line) @@ -311,6 +319,11 @@ class TRexConsole(TRexGeneralCmd): ############# stop def do_stop(self, line): '''stops port(s) transmitting traffic\n''' + + if not self.stateless_client.is_connected(): + print format_text("\nNot connected to server\n", 'bold') + return + self.stateless_client.cmd_stop_line(line) def help_stop(self): @@ -319,6 +332,11 @@ class TRexConsole(TRexGeneralCmd): ############# update def do_update(self, line): '''update speed of port(s)currently transmitting traffic\n''' + + if not self.stateless_client.is_connected(): + print format_text("\nNot connected to server\n", 'bold') + return + self.stateless_client.cmd_update_line(line) def help_update (self): @@ -327,11 +345,21 @@ class TRexConsole(TRexGeneralCmd): ############# pause def do_pause(self, line): '''pause port(s) transmitting traffic\n''' + + if not self.stateless_client.is_connected(): + print format_text("\nNot connected to server\n", 'bold') + return + self.stateless_client.cmd_pause_line(line) ############# resume def do_resume(self, line): '''resume port(s) transmitting traffic\n''' + + if not self.stateless_client.is_connected(): + print format_text("\nNot connected to server\n", 'bold') + return + self.stateless_client.cmd_resume_line(line) @@ -339,6 +367,11 @@ class TRexConsole(TRexGeneralCmd): ########## reset def do_reset (self, line): '''force stop all ports\n''' + + if not self.stateless_client.is_connected(): + print format_text("\nNot connected to server\n", 'bold') + return + self.stateless_client.cmd_reset() -- cgit From a9f6028ef1214bfed69efd0aff59131327a06b8e Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Sat, 28 Nov 2015 00:01:41 +0200 Subject: fix second try of running daemon: second daemonize_parser() ruins input args --- .../trex_control_plane/server/extended_daemon_runner.py | 7 +++++-- .../automation/trex_control_plane/server/trex_daemon_server.py | 10 +--------- 2 files changed, 6 insertions(+), 11 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/server/extended_daemon_runner.py b/scripts/automation/trex_control_plane/server/extended_daemon_runner.py index 734fa22e..a374db30 100755 --- a/scripts/automation/trex_control_plane/server/extended_daemon_runner.py +++ b/scripts/automation/trex_control_plane/server/extended_daemon_runner.py @@ -19,7 +19,6 @@ def daemonize_parser(parser_obj, action_funcs, help_menu): parser_obj.usage = None parser_obj.add_argument("action", choices=action_funcs, action="store", help=help_menu) - return class ExtendedDaemonRunner(runner.DaemonRunner): @@ -76,7 +75,11 @@ class ExtendedDaemonRunner(runner.DaemonRunner): self.app = app self.daemon_context = daemon.DaemonContext() self.daemon_context.stdin = open(app.stdin_path, 'rt') - self.daemon_context.stdout = open(app.stdout_path, 'w+t') + try: + self.daemon_context.stdout = open(app.stdout_path, 'w+t') + except IOError as err: + app.stdout_path = "/dev/null" + self.daemon_context.stdout = open(app.stdout_path, 'w+t') self.daemon_context.stderr = open(app.stderr_path, 'a+t', buffering=0) diff --git a/scripts/automation/trex_control_plane/server/trex_daemon_server.py b/scripts/automation/trex_control_plane/server/trex_daemon_server.py index ec07cb8a..9784d42a 100755 --- a/scripts/automation/trex_control_plane/server/trex_daemon_server.py +++ b/scripts/automation/trex_control_plane/server/trex_daemon_server.py @@ -57,15 +57,7 @@ def main (): print "Launching user must have sudo privileges in order to run TRex daemon.\nTerminating daemon process." exit(-1) - try: - daemon_runner = ExtendedDaemonRunner(trex_app, trex_parser) - except IOError as err: - # catch 'tty' error when launching server from remote location - if err.errno == errno.ENXIO: - trex_app.stdout_path = "/dev/null" - daemon_runner = ExtendedDaemonRunner(trex_app, trex_parser) - else: - raise + daemon_runner = ExtendedDaemonRunner(trex_app, trex_parser) #This ensures that the logger file handle does not get closed during daemonization daemon_runner.daemon_context.files_preserve=[handler.stream] -- cgit From 54a8bafe065785fb6c14ff7a4d5b1478e124addc Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Sat, 28 Nov 2015 00:06:33 +0200 Subject: restore comment --- scripts/automation/trex_control_plane/server/extended_daemon_runner.py | 1 + 1 file changed, 1 insertion(+) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/server/extended_daemon_runner.py b/scripts/automation/trex_control_plane/server/extended_daemon_runner.py index a374db30..7bc25aac 100755 --- a/scripts/automation/trex_control_plane/server/extended_daemon_runner.py +++ b/scripts/automation/trex_control_plane/server/extended_daemon_runner.py @@ -78,6 +78,7 @@ class ExtendedDaemonRunner(runner.DaemonRunner): try: self.daemon_context.stdout = open(app.stdout_path, 'w+t') except IOError as err: + # catch 'tty' error when launching server from remote location app.stdout_path = "/dev/null" self.daemon_context.stdout = open(app.stdout_path, 'w+t') self.daemon_context.stderr = open(app.stderr_path, -- cgit From a609111bc37ef88f14d4f2ebf7cd186b04b86402 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 29 Nov 2015 00:25:07 +0200 Subject: Supports all desired stats option, plus clearing option --- .../trex_control_plane/client/trex_async_client.py | 3 + .../client/trex_stateless_client.py | 48 +++++-- .../trex_control_plane/common/trex_stats.py | 154 ++++++++++++++++++--- .../trex_control_plane/console/trex_console.py | 7 + 4 files changed, 180 insertions(+), 32 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index 0a3afbe8..a2bb4752 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -46,6 +46,9 @@ class CTRexAsyncStats(object): if self.ref_point == None: self.ref_point = self.current + + def clear(self): + self.ref_point = self.current def get(self, field, format = False, suffix = ""): diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 2db30daf..7be7392e 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -148,6 +148,13 @@ class Port(object): STATE_STREAMS = 2 STATE_TX = 3 STATE_PAUSE = 4 + PortState = namedtuple('PortState', ['state_id', 'state_name']) + STATES_MAP = {STATE_DOWN: "DOWN", + STATE_IDLE: "IDLE", + STATE_STREAMS: "STREAMS", + STATE_TX: "ACTIVE", + STATE_PAUSE: "PAUSE"} + def __init__ (self, port_id, speed, driver, user, transmit): self.port_id = port_id @@ -399,6 +406,10 @@ class Port(object): return self.ok() + def get_port_state_name(self): + return self.STATES_MAP.get(self.state, "Unknown") + + ################# events handler ###################### def async_event_port_stopped (self): self.state = self.STATE_STREAMS @@ -423,7 +434,8 @@ class CTRexStatelessClient(object): self._async_client = CTRexAsyncClient(server, async_port, self) self.streams_db = CStreamsDB() - self.info_and_stats = trex_stats.CTRexInformationCenter({"server": server, + self.info_and_stats = trex_stats.CTRexInformationCenter(self.user, + {"server": server, "sync_port": sync_port, "async_port": async_port}, self.ports, @@ -885,7 +897,7 @@ class CTRexStatelessClient(object): active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) if not active_ports: - msg = "No active traffic on porvided ports" + msg = "No active traffic on provided ports" print format_text(msg, 'bold') return RC_ERR(msg) @@ -896,6 +908,11 @@ class CTRexStatelessClient(object): return RC_OK() + def cmd_clear(self, port_id_list): + self.info_and_stats.clear(port_id_list) + return RC_OK() + + # pause cmd def cmd_pause (self, port_id_list): @@ -999,16 +1016,13 @@ class CTRexStatelessClient(object): return RC_OK() def cmd_stats(self, port_id_list, stats_mask=set()): - print port_id_list - print stats_mask stats_opts = trex_stats.ALL_STATS_OPTS.intersection(stats_mask) - print stats_opts stats_obj = {} for stats_type in stats_opts: - stats_obj.update(self.info_and_stats.generate_single_statistic(stats_type)) + stats_obj.update(self.info_and_stats.generate_single_statistic(port_id_list, stats_type)) return stats_obj - pass + ############## High Level API With Parser ################ def cmd_start_line (self, line): @@ -1090,6 +1104,19 @@ class CTRexStatelessClient(object): def cmd_reset_line (self, line): return self.cmd_reset() + def cmd_clear_line (self, line): + '''Clear cached local statistics\n''' + # define a parser + parser = parsing_opts.gen_parser(self, + "clear", + self.cmd_clear_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + + opts = parser.parse_args(line.split()) + + if opts is None: + return RC_ERR("bad command line parameters") + return self.cmd_clear(opts.ports) def cmd_stats_line (self, line): '''Fetch statistics from TRex server by port\n''' @@ -1105,10 +1132,11 @@ class CTRexStatelessClient(object): if opts is None: return RC_ERR("bad command line parameters") - print opts - print self.get_global_stats() # determine stats mask - mask = self._get_mask_keys(**self._filter_namespace_args(opts, ['p', 'g', 'ps'])) + mask = self._get_mask_keys(**self._filter_namespace_args(opts, trex_stats.ALL_STATS_OPTS)) + if not mask: + # set to show all stats if no filter was given + mask = trex_stats.ALL_STATS_OPTS # get stats objects, as dictionary stats = self.cmd_stats(opts.ports, mask) # print stats to screen diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py index bf5ba2bb..ec455730 100755 --- a/scripts/automation/trex_control_plane/common/trex_stats.py +++ b/scripts/automation/trex_control_plane/common/trex_stats.py @@ -1,6 +1,8 @@ #!/router/bin/python from collections import namedtuple, OrderedDict from client_utils import text_tables +from common.text_opts import format_text +from client.trex_async_client import CTRexAsyncStats import copy GLOBAL_STATS = 'g' @@ -12,7 +14,8 @@ ExportableStats = namedtuple('ExportableStats', ['raw_data', 'text_table']) class CTRexInformationCenter(object): - def __init__(self, connection_info, ports_ref, async_stats_ref): + def __init__(self, username, connection_info, ports_ref, async_stats_ref): + self.user = username self.connection_info = connection_info self.server_version = None self.system_info = None @@ -26,14 +29,20 @@ class CTRexInformationCenter(object): # else: # return None - def generate_single_statistic(self, statistic_type): + def clear(self, port_id_list): + self._async_stats.get_general_stats().clear() + for port_id in port_id_list: + self._async_stats.get_port_stats(port_id).clear() + pass + + def generate_single_statistic(self, port_id_list, statistic_type): if statistic_type == GLOBAL_STATS: return self._generate_global_stats() elif statistic_type == PORT_STATS: - # return generate_global_stats() + return self._generate_port_stats(port_id_list) pass elif statistic_type == PORT_STATUS: - pass + return self._generate_port_status(port_id_list) else: # ignore by returning empty object return {} @@ -43,26 +52,11 @@ class CTRexInformationCenter(object): return_stats_data = \ OrderedDict([("connection", "{host}, Port {port}".format(host=self.connection_info.get("server"), port=self.connection_info.get("sync_port"))), - ("version", self.server_version.get("version", "N/A")), - ("cpu_util", stats_obj.get("m_cpu_util")), + ("version", "{ver}, UUID: {uuid}".format(ver=self.server_version.get("version", "N/A"), + uuid="N/A")), + ("cpu_util", "{0}%".format(stats_obj.get("m_cpu_util"))), ("total_tx", stats_obj.get("m_tx_bps", format=True, suffix="b/sec")), - # {'m_tx_bps': stats_obj.get("m_tx_bps", format= True, suffix= "b/sec"), - # 'm_tx_pps': stats_obj.get("m_tx_pps", format= True, suffix= "pkt/sec"), - # 'm_total_tx_bytes':stats_obj.get_rel("m_total_tx_bytes", - # format= True, - # suffix = "B"), - # 'm_total_tx_pkts': stats_obj.get_rel("m_total_tx_pkts", - # format= True, - # suffix = "pkts")}, ("total_rx", stats_obj.get("m_rx_bps", format=True, suffix="b/sec")), - # {'m_rx_bps': stats_obj.get("m_rx_bps", format= True, suffix= "b/sec"), - # 'm_rx_pps': stats_obj.get("m_rx_pps", format= True, suffix= "pkt/sec"), - # 'm_total_rx_bytes': stats_obj.get_rel("m_total_rx_bytes", - # format= True, - # suffix = "B"), - # 'm_total_rx_pkts': stats_obj.get_rel("m_total_rx_pkts", - # format= True, - # suffix = "pkts")}, ("total_pps", stats_obj.format_num(stats_obj.get("m_tx_pps") + stats_obj.get("m_rx_pps"), suffix="pkt/sec")), ("total_streams", sum([len(port.streams) @@ -81,6 +75,122 @@ class CTRexInformationCenter(object): return {"global_statistics": ExportableStats(return_stats_data, stats_table)} + def _generate_port_stats(self, port_id_list): + relevant_ports = self.__get_relevant_ports(port_id_list) + + return_stats_data = {} + per_field_stats = OrderedDict([("owner", []), + ("active", []), + ("tx-bytes", []), + ("rx-bytes", []), + ("tx-pkts", []), + ("rx-pkts", []), + ("tx-errors", []), + ("rx-errors", []), + ("tx-BW", []), + ("rx-BW", []) + ] + ) + + for port_obj in relevant_ports: + # fetch port data + port_stats = self._async_stats.get_port_stats(port_obj.port_id) + + owner = self.user + active = "YES" if port_obj.is_active() else "NO" + tx_bytes = port_stats.get_rel("obytes", format = True, suffix = "B") + rx_bytes = port_stats.get_rel("ibytes", format = True, suffix = "B") + tx_pkts = port_stats.get_rel("opackets", format = True, suffix = "pkts") + rx_pkts = port_stats.get_rel("ipackets", format = True, suffix = "pkts") + tx_errors = port_stats.get_rel("oerrors", format = True) + rx_errors = port_stats.get_rel("ierrors", format = True) + tx_bw = port_stats.get("m_total_tx_bps", format = True, suffix = "bps") + rx_bw = port_stats.get("m_total_rx_bps", format = True, suffix = "bps") + + # populate to data structures + return_stats_data[port_obj.port_id] = {"owner": owner, + "active": active, + "tx-bytes": tx_bytes, + "rx-bytes": rx_bytes, + "tx-pkts": tx_pkts, + "rx-pkts": rx_pkts, + "tx-errors": tx_errors, + "rx-errors": rx_errors, + "Tx-BW": tx_bw, + "Rx-BW": rx_bw + } + per_field_stats["owner"].append(owner) + per_field_stats["active"].append(active) + per_field_stats["tx-bytes"].append(tx_bytes) + per_field_stats["rx-bytes"].append(rx_bytes) + per_field_stats["tx-pkts"].append(tx_pkts) + per_field_stats["rx-pkts"].append(rx_pkts) + per_field_stats["tx-errors"].append(tx_errors) + per_field_stats["rx-errors"].append(rx_errors) + per_field_stats["tx-BW"].append(tx_bw) + per_field_stats["rx-BW"].append(rx_bw) + + stats_table = text_tables.TRexTextTable() + stats_table.set_cols_align(["l"] + ["r"]*len(relevant_ports)) + stats_table.add_rows([[k] + v + for k, v in per_field_stats.iteritems()], + header=False) + stats_table.header(["port"] + [port.port_id + for port in relevant_ports]) + + return {"port_statistics": ExportableStats(return_stats_data, stats_table)} + + def _generate_port_status(self, port_id_list): + relevant_ports = self.__get_relevant_ports(port_id_list) + + return_stats_data = {} + per_field_status = OrderedDict([("port-type", []), + ("maximum", []), + ("port-status", []) + ] + ) + + for port_obj in relevant_ports: + # fetch port data + port_stats = self._async_stats.get_port_stats(port_obj.port_id) + + + port_type = port_obj.driver + maximum = "{speed} Gb/s".format(speed=port_obj.speed)#CTRexAsyncStats.format_num(port_obj.get_speed_bps(), suffix="bps") + port_status = port_obj.get_port_state_name() + + # populate to data structures + return_stats_data[port_obj.port_id] = {"port-type": port_type, + "maximum": maximum, + "port-status": port_status, + } + per_field_status["port-type"].append(port_type) + per_field_status["maximum"].append(maximum) + per_field_status["port-status"].append(port_status) + + stats_table = text_tables.TRexTextTable() + stats_table.set_cols_align(["l"] + ["c"]*len(relevant_ports)) + stats_table.add_rows([[k] + v + for k, v in per_field_status.iteritems()], + header=False) + stats_table.header(["port"] + [port.port_id + for port in relevant_ports]) + + return {"port_status": ExportableStats(return_stats_data, stats_table)} + + def __get_relevant_ports(self, port_id_list): + # fetch owned ports + ports = [port_obj + for port_obj in self._ports + if port_obj.is_acquired() and port_obj.port_id in port_id_list] + # display only the first FOUR options, by design + if len(ports) > 4: + print format_text("[WARNING]: ", 'magenta', 'bold'), format_text("displaying up to 4 ports", 'magenta') + ports = ports[:4] + return ports + + + class CTRexStatsManager(object): diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 3ddfd8c6..9236ce98 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -350,6 +350,13 @@ class TRexConsole(TRexGeneralCmd): def help_stats(self): self.do_stats("-h") + def do_clear(self, line): + '''Clear cached local statistics\n''' + self.stateless_client.cmd_clear_line(line) + + def help_clear(self): + self.do_clear("-h") + def help_events (self): self.do_events("-h") -- cgit From 4c94931c5de8673433d3bf22999ecc84d41e0595 Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 29 Nov 2015 08:38:38 -0500 Subject: support for update +/- request --- .../client/trex_stateless_client.py | 19 +---- .../trex_control_plane/console/parsing_opts.py | 89 ++++++++++++++-------- 2 files changed, 62 insertions(+), 46 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index b3476268..c1dea9eb 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -292,14 +292,6 @@ class Port(object): def get_all_streams (self): return self.streams - - def process_mul (self, mul): - # if percentage - translate - if mul['type'] == 'percentage': - mul['type'] = 'max_bps' - mul['max'] = self.get_speed_bps() * (mul['max'] / 100) - - # start traffic def start (self, mul, duration): if self.state == self.STATE_DOWN: @@ -311,8 +303,6 @@ class Port(object): if self.state == self.STATE_TX: return self.err("Unable to start traffic - port is already transmitting") - self.process_mul(mul) - params = {"handler": self.handler, "port_id": self.port_id, "mul": mul, @@ -385,8 +375,6 @@ class Port(object): if (self.state != self.STATE_TX) : return self.err("port is not transmitting") - self.process_mul(mul) - params = {"handler": self.handler, "port_id": self.port_id, "mul": mul} @@ -1018,13 +1006,14 @@ class CTRexStatelessClient(object): parsing_opts.FORCE, parsing_opts.STREAM_FROM_PATH_OR_FILE, parsing_opts.DURATION, - parsing_opts.MULTIPLIER) + parsing_opts.MULTIPLIER_STRICT) opts = parser.parse_args(line.split()) if opts is None: return RC_ERR("bad command line paramters") + if opts.db: stream_list = self.stream_db.get_stream_pack(opts.db) rc = RC(stream_list != None) @@ -1044,7 +1033,7 @@ class CTRexStatelessClient(object): # total has no meaning with percentage - its linear if opts.total and (opts.mult['type'] != 'percentage'): # if total was set - divide it between the ports - opts.mult['max'] = opts.mult['max'] / len(opts.ports) + opts.mult['value'] = opts.mult['value'] / len(opts.ports) return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force, opts.duration) @@ -1078,7 +1067,7 @@ class CTRexStatelessClient(object): # total has no meaning with percentage - its linear if opts.total and (opts.mult['type'] != 'percentage'): # if total was set - divide it between the ports - opts.mult['max'] = opts.mult['max'] / len(opts.ports) + opts.mult['value'] = opts.mult['value'] / len(opts.ports) return self.cmd_update(opts.ports, opts.mult) diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index 0bcdce84..d7bf583a 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -10,16 +10,17 @@ ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options']) # list of available parsing options MULTIPLIER = 1 -PORT_LIST = 2 -ALL_PORTS = 3 -PORT_LIST_WITH_ALL = 4 -FILE_PATH = 5 -FILE_FROM_DB = 6 -SERVER_IP = 7 -STREAM_FROM_PATH_OR_FILE = 8 -DURATION = 9 -FORCE = 10 -TOTAL = 11 +MULTIPLIER_STRICT = 2 +PORT_LIST = 3 +ALL_PORTS = 4 +PORT_LIST_WITH_ALL = 5 +FILE_PATH = 6 +FILE_FROM_DB = 7 +SERVER_IP = 8 +STREAM_FROM_PATH_OR_FILE = 9 +DURATION = 10 +FORCE = 11 +TOTAL = 12 # list of ArgumentGroup types MUTEX = 1 @@ -54,10 +55,15 @@ match_multiplier_help = """Multiplier should be passed in the following format: will provide a percentage of the line rate. examples : '-m 10', '-m 10kbps', '-m 10mpps', '-m 23%%' """ -def match_multiplier(val): - '''match some val against multiplier shortcut inputs ''' +def match_multiplier_common(val, strict_abs = True): - match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)$", val) + # on strict absolute we do not allow +/- + if strict_abs: + match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)$", val) + op = None + else: + match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)([\+\-])?$", val) + op = match.group(4) result = {} @@ -65,44 +71,53 @@ def match_multiplier(val): value = float(match.group(1)) unit = match.group(3) + + # raw type (factor) if not unit: result['type'] = 'raw' - result['max'] = value + result['value'] = value elif unit == 'bps': - result['type'] = 'max_bps' - result['max'] = value + result['type'] = 'bps' + result['value'] = value elif unit == 'kbps': - result['type'] = 'max_bps' - result['max'] = value * 1000 + result['type'] = 'bps' + result['value'] = value * 1000 elif unit == 'mbps': - result['type'] = 'max_bps' - result['max'] = value * 1000 * 1000 + result['type'] = 'bps' + result['value'] = value * 1000 * 1000 elif unit == 'gbps': - result['type'] = 'max_bps' - result['max'] = value * 1000 * 1000 * 1000 + result['type'] = 'bps' + result['value'] = value * 1000 * 1000 * 1000 elif unit == 'pps': - result['type'] = 'max_pps' - result['max'] = value + result['type'] = 'pps' + result['value'] = value elif unit == "kpps": - result['type'] = 'max_pps' - result['max'] = value * 1000 + result['type'] = 'pps' + result['value'] = value * 1000 elif unit == "mpps": - result['type'] = 'max_pps' - result['max'] = value * 1000 * 1000 + result['type'] = 'pps' + result['value'] = value * 1000 * 1000 elif unit == "%": - # will be translated by the port object result['type'] = 'percentage' - result['max'] = value + result['value'] = value + + + if op == "+": + result['op'] = "add" + elif op == "-": + result['op'] = "sub" + else: + result['op'] = "abs" return result @@ -110,6 +125,13 @@ def match_multiplier(val): raise argparse.ArgumentTypeError(match_multiplier_help) +def match_multiplier(val): + '''match some val against multiplier shortcut inputs ''' + return match_multiplier_common(val, strict_abs = False) + +def match_multiplier_strict(val): + '''match some val against multiplier shortcut inputs ''' + return match_multiplier_common(val, strict_abs = True) def is_valid_file(filename): if not os.path.isfile(filename): @@ -121,9 +143,14 @@ def is_valid_file(filename): OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'help': match_multiplier_help, 'dest': "mult", - 'default': {'type':'raw', 'max':1}, + 'default': {'type':'raw', 'value':1, 'op': 'abs'}, 'type': match_multiplier}), + MULTIPLIER_STRICT: ArgumentPack(['-m', '--multiplier'], + {'help': match_multiplier_help, + 'dest': "mult", + 'default': {'type':'raw', 'value':1, 'op': 'abs'}, + 'type': match_multiplier_strict}), TOTAL: ArgumentPack(['-t', '--total'], {'help': "traffic will be divided between all ports specified", -- cgit From a48cd6471a2d82e5d78e8abe85b065f66a388e11 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 1 Dec 2015 03:46:21 -0500 Subject: 1. fixed ZMQ message limitation 2. added some scale yamls for IMIX (300 streams, 1000 streams) 3. return objects are always complex objects (not strings) - for backward compatability 4. some minor adjustments to ZMQ socket timeouts --- .../client/trex_stateless_client.py | 77 ++++++++++++++++++---- .../client_utils/jsonrpc_client.py | 8 +-- .../trex_control_plane/common/trex_streams.py | 16 ++++- .../trex_control_plane/console/trex_console.py | 2 +- 4 files changed, 82 insertions(+), 21 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index c1dea9eb..4436be75 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -56,7 +56,7 @@ class RC: def annotate (self, desc = None): if desc: - print format_text('\n{:<40}'.format(desc), 'bold'), + print format_text('\n{:<60}'.format(desc), 'bold'), if self.bad(): # print all the errors @@ -147,11 +147,13 @@ class Port(object): STATE_TX = 3 STATE_PAUSE = 4 - def __init__ (self, port_id, speed, driver, user, transmit): + def __init__ (self, port_id, speed, driver, user, comm_link): self.port_id = port_id self.state = self.STATE_IDLE self.handler = None - self.transmit = transmit + self.comm_link = comm_link + self.transmit = comm_link.transmit + self.transmit_batch = comm_link.transmit_batch self.user = user self.driver = driver self.speed = speed @@ -249,6 +251,33 @@ class Port(object): return self.ok() + # add multiple streams + def add_streams (self, streams_list): + batch = [] + + for stream in streams_list: + params = {"handler": self.handler, + "port_id": self.port_id, + "stream_id": stream.stream_id, + "stream": stream.stream} + + cmd = RpcCmdData('add_stream', params) + batch.append(cmd) + + rc, data = self.transmit_batch(batch) + + if not rc: + return self.err(data) + + # add the stream + for stream in streams_list: + self.streams[stream.stream_id] = stream.stream + + # the only valid state now + self.state = self.STATE_STREAMS + + return self.ok() + # remove stream from port def remove_stream (self, stream_id): @@ -485,6 +514,24 @@ class CTRexStatelessClient(object): ############# helper functions section ############## + # measure time for functions + def timing(f): + def wrap(*args): + time1 = time.time() + ret = f(*args) + delta = time.time() - time1 + + for unit in ['sec','ms','usec']: + + if delta > 1.0: + print '{:,.2f} [{:}]\n'.format(delta, unit) + break + delta *= 1000.0 + return ret + + return wrap + + def validate_port_list(self, port_id_list): if not isinstance(port_id_list, list): print type(port_id_list) @@ -551,7 +598,7 @@ class CTRexStatelessClient(object): for port_id in xrange(self.get_port_count()): speed = self.system_info['ports'][port_id]['speed'] driver = self.system_info['ports'][port_id]['driver'] - self.ports.append(Port(port_id, speed, driver, self.user, self.transmit)) + self.ports.append(Port(port_id, speed, driver, self.user, self.comm_link)) # acquire all ports rc = self.acquire() @@ -689,15 +736,16 @@ class CTRexStatelessClient(object): return rc + def add_stream_pack(self, stream_pack_list, port_id_list = None): port_id_list = self.__ports(port_id_list) rc = RC() - for stream_pack in stream_pack_list: - rc.add(self.add_stream(stream_pack.stream_id, stream_pack.stream, port_id_list)) - + for port_id in port_id_list: + rc.add(self.ports[port_id].add_streams(stream_pack_list)) + return rc @@ -805,9 +853,12 @@ class CTRexStatelessClient(object): return self.comm_link.transmit(method_name, params) + def transmit_batch(self, batch_list): + return self.comm_link.transmit_batch(batch_list) ######################### Console (high level) API ######################### + @timing def cmd_ping(self): rc = self.ping() rc.annotate("Pinging the server on '{0}' port '{1}': ".format(self.get_connection_ip(), self.get_connection_port())) @@ -957,7 +1008,6 @@ class CTRexStatelessClient(object): return self.cmd_resume(opts.ports) - # start cmd def cmd_start (self, port_id_list, stream_list, mult, force, duration): @@ -981,11 +1031,10 @@ class CTRexStatelessClient(object): rc = self.add_stream_pack(stream_list.compiled, port_id_list) - rc.annotate("Attaching streams to port(s) {0}:".format(port_id_list)) + rc.annotate("Attaching {0} streams to port(s) {1}:".format(len(stream_list.compiled), port_id_list)) if rc.bad(): return rc - # finally, start the traffic rc = self.start_traffic(mult, duration, port_id_list) rc.annotate("Starting traffic on port(s) {0}:".format(port_id_list)) @@ -995,6 +1044,7 @@ class CTRexStatelessClient(object): return RC_OK() ############## High Level API With Parser ################ + @timing def cmd_start_line (self, line): '''Start selected traffic in specified ports on TRex\n''' # define a parser @@ -1037,6 +1087,7 @@ class CTRexStatelessClient(object): return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force, opts.duration) + @timing def cmd_stop_line (self, line): '''Stop active traffic in specified ports on TRex\n''' parser = parsing_opts.gen_parser(self, @@ -1050,7 +1101,7 @@ class CTRexStatelessClient(object): return self.cmd_stop(opts.ports) - + @timing def cmd_update_line (self, line): '''Update port(s) speed currently active\n''' parser = parsing_opts.gen_parser(self, @@ -1071,11 +1122,11 @@ class CTRexStatelessClient(object): return self.cmd_update(opts.ports, opts.mult) - + @timing def cmd_reset_line (self, line): return self.cmd_reset() - + def cmd_exit_line (self, line): print format_text("Exiting\n", 'bold') # a way to exit diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index b826f02f..a5789c46 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -37,7 +37,7 @@ class BatchMessage(object): msg = json.dumps(self.batch_list) - rc, resp_list = self.rpc_client.send_raw_msg(msg, block = False) + rc, resp_list = self.rpc_client.send_raw_msg(msg) if len(self.batch_list) == 1: return CmdResponse(True, [CmdResponse(rc, resp_list)]) else: @@ -130,7 +130,6 @@ class JsonRpcClient(object): self.socket.send(msg) break except zmq.Again: - sleep(0.1) tries += 1 if tries > 10: self.disconnect() @@ -143,7 +142,6 @@ class JsonRpcClient(object): response = self.socket.recv() break except zmq.Again: - sleep(0.1) tries += 1 if tries > 10: self.disconnect() @@ -223,8 +221,8 @@ class JsonRpcClient(object): except zmq.error.ZMQError as e: return False, "ZMQ Error: Bad server or port name: " + str(e) - self.socket.setsockopt(zmq.SNDTIMEO, 5) - self.socket.setsockopt(zmq.RCVTIMEO, 5) + self.socket.setsockopt(zmq.SNDTIMEO, 1000) + self.socket.setsockopt(zmq.RCVTIMEO, 1000) self.connected = True diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py index bb4c72ca..c2823445 100755 --- a/scripts/automation/trex_control_plane/common/trex_streams.py +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -18,10 +18,22 @@ class CStreamList(object): self.yaml_loader = CTRexYAMLLoader(os.path.join(os.path.dirname(os.path.realpath(__file__)), "rpc_defaults.yaml")) + def generate_numbered_name (self, name): + prefix = name.rstrip('01234567890') + suffix = name[len(prefix):] + if suffix == "": + n = "_1" + else: + n = int(suffix) + 1 + return prefix + str(n) + def append_stream(self, name, stream_obj): assert isinstance(stream_obj, CStream) - if name in self.streams_list: - raise NameError("A stream with this name already exists on this list.") + + # if name exists simply add numbered suffix to it + while name in self.streams_list: + name = self.generate_numbered_name(name) + self.streams_list[name]=stream_obj return name diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index e537c306..be8fb70e 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -372,7 +372,7 @@ class TRexConsole(TRexGeneralCmd): print format_text("\nNot connected to server\n", 'bold') return - self.stateless_client.cmd_reset() + self.stateless_client.cmd_reset_line(line) def help_events (self): -- cgit From a6af2a8e624c62d9a347215321c6562f28879d97 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 3 Dec 2015 06:07:20 -0500 Subject: various fixes (each one is a minor one) --- .../client/trex_stateless_client.py | 112 +++++++++++++++------ .../trex_control_plane/common/trex_streams.py | 3 +- .../trex_control_plane/console/trex_console.py | 65 ++++++------ 3 files changed, 115 insertions(+), 65 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 4436be75..6082863e 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -414,6 +414,17 @@ class Port(object): return self.ok() + + def validate (self): + params = {"handler": self.handler, + "port_id": self.port_id} + + rc, data = self.transmit("validate", params) + if not rc: + return self.err(data) + + return self.ok() + ################# events handler ###################### def async_event_port_stopped (self): self.state = self.STATE_STREAMS @@ -519,6 +530,11 @@ class CTRexStatelessClient(object): def wrap(*args): time1 = time.time() ret = f(*args) + + # don't want to print on error + if ret.bad(): + return ret + delta = time.time() - time1 for unit in ['sec','ms','usec']: @@ -842,6 +858,17 @@ class CTRexStatelessClient(object): return rc + def validate (self, port_id_list = None): + port_id_list = self.__ports(port_id_list) + + rc = RC() + + for port_id in port_id_list: + rc.add(self.ports[port_id].validate()) + + return rc + + def get_port_stats(self, port_id=None): pass @@ -962,19 +989,6 @@ class CTRexStatelessClient(object): return RC_OK() - def cmd_pause_line (self, line): - '''Pause active traffic in specified ports on TRex\n''' - parser = parsing_opts.gen_parser(self, - "pause", - self.cmd_stop_line.__doc__, - parsing_opts.PORT_LIST_WITH_ALL) - - opts = parser.parse_args(line.split()) - if opts is None: - return RC_ERR("bad command line paramters") - - return self.cmd_pause(opts.ports) - # resume cmd def cmd_resume (self, port_id_list): @@ -995,19 +1009,6 @@ class CTRexStatelessClient(object): return RC_OK() - def cmd_resume_line (self, line): - '''Resume active traffic in specified ports on TRex\n''' - parser = parsing_opts.gen_parser(self, - "resume", - self.cmd_stop_line.__doc__, - parsing_opts.PORT_LIST_WITH_ALL) - - opts = parser.parse_args(line.split()) - if opts is None: - return RC_ERR("bad command line paramters") - - return self.cmd_resume(opts.ports) - # start cmd def cmd_start (self, port_id_list, stream_list, mult, force, duration): @@ -1043,6 +1044,15 @@ class CTRexStatelessClient(object): return RC_OK() + + def cmd_validate (self, port_id_list): + rc = self.validate(port_id_list) + rc.annotate("Validating streams on port(s) {0}:".format(port_id_list)) + if rc.bad(): + return rc + + return RC_OK() + ############## High Level API With Parser ################ @timing def cmd_start_line (self, line): @@ -1126,7 +1136,53 @@ class CTRexStatelessClient(object): def cmd_reset_line (self, line): return self.cmd_reset() - + + @timing + def cmd_pause_line (self, line): + '''Pause active traffic in specified ports on TRex\n''' + parser = parsing_opts.gen_parser(self, + "pause", + self.cmd_stop_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + + opts = parser.parse_args(line.split()) + if opts is None: + return RC_ERR("bad command line paramters") + + return self.cmd_pause(opts.ports) + + + @timing + def cmd_resume_line (self, line): + '''Resume active traffic in specified ports on TRex\n''' + parser = parsing_opts.gen_parser(self, + "resume", + self.cmd_stop_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + + opts = parser.parse_args(line.split()) + if opts is None: + return RC_ERR("bad command line paramters") + + return self.cmd_resume(opts.ports) + + + @timing + def cmd_validate_line (self, line): + '''validates port(s) stream configuration\n''' + + parser = parsing_opts.gen_parser(self, + "validate", + self.cmd_validate_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + + opts = parser.parse_args(line.split()) + if opts is None: + return RC_ERR("bad command line paramters") + + return self.cmd_validate(opts.ports) + + def cmd_exit_line (self, line): print format_text("Exiting\n", 'bold') # a way to exit @@ -1262,5 +1318,3 @@ class CTRexStatelessClient(object): if __name__ == "__main__": pass - - \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py index c2823445..89de7286 100755 --- a/scripts/automation/trex_control_plane/common/trex_streams.py +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -14,7 +14,7 @@ StreamPack = namedtuple('StreamPack', ['stream_id', 'stream']) class CStreamList(object): def __init__(self): - self.streams_list = {} + self.streams_list = OrderedDict() self.yaml_loader = CTRexYAMLLoader(os.path.join(os.path.dirname(os.path.realpath(__file__)), "rpc_defaults.yaml")) @@ -82,6 +82,7 @@ class CStreamList(object): stream_ids = {} for idx, stream_name in enumerate(self.streams_list): stream_ids[stream_name] = idx + # next, iterate over the streams and transform them from working with names to ids. # with that build a new dict with old stream_name as the key, and StreamPack as the stored value compiled_streams = {} diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index be8fb70e..73f4f612 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -35,7 +35,7 @@ from common.text_opts import * from client_utils.general_utils import user_input, get_current_user import trex_status import parsing_opts - +from functools import wraps __version__ = "1.1" @@ -128,6 +128,18 @@ class TRexConsole(TRexGeneralCmd): ################### internal section ######################## + def verify_connected(f): + @wraps(f) + def wrap(*args): + inst = args[0] + if not inst.stateless_client.is_connected(): + print format_text("\nNot connected to server\n", 'bold') + return + + ret = f(*args) + return ret + + return wrap def get_console_identifier(self): return "{context}_{server}".format(context=self.__class__.__name__, @@ -207,13 +219,9 @@ class TRexConsole(TRexGeneralCmd): ####################### shell commands ####################### + @verify_connected def do_ping (self, line): '''Ping the server\n''' - - if not self.stateless_client.is_connected(): - print format_text("\nNot connected to server\n", 'bold') - return - rc = self.stateless_client.cmd_ping() if rc.bad(): return @@ -303,13 +311,10 @@ class TRexConsole(TRexGeneralCmd): if (l > 2) and (s[l - 2] in file_flags): return TRexConsole.tree_autocomplete(s[l - 1]) + @verify_connected def do_start(self, line): '''Start selected traffic in specified port(s) on TRex\n''' - if not self.stateless_client.is_connected(): - print format_text("\nNot connected to server\n", 'bold') - return - self.stateless_client.cmd_start_line(line) @@ -317,64 +322,57 @@ class TRexConsole(TRexGeneralCmd): self.do_start("-h") ############# stop + @verify_connected def do_stop(self, line): '''stops port(s) transmitting traffic\n''' - if not self.stateless_client.is_connected(): - print format_text("\nNot connected to server\n", 'bold') - return - self.stateless_client.cmd_stop_line(line) def help_stop(self): self.do_stop("-h") ############# update + @verify_connected def do_update(self, line): '''update speed of port(s)currently transmitting traffic\n''' - if not self.stateless_client.is_connected(): - print format_text("\nNot connected to server\n", 'bold') - return - self.stateless_client.cmd_update_line(line) def help_update (self): self.do_update("-h") ############# pause + @verify_connected def do_pause(self, line): '''pause port(s) transmitting traffic\n''' - if not self.stateless_client.is_connected(): - print format_text("\nNot connected to server\n", 'bold') - return - self.stateless_client.cmd_pause_line(line) ############# resume + @verify_connected def do_resume(self, line): '''resume port(s) transmitting traffic\n''' - if not self.stateless_client.is_connected(): - print format_text("\nNot connected to server\n", 'bold') - return - self.stateless_client.cmd_resume_line(line) ########## reset + @verify_connected def do_reset (self, line): '''force stop all ports\n''' - if not self.stateless_client.is_connected(): - print format_text("\nNot connected to server\n", 'bold') - return - self.stateless_client.cmd_reset_line(line) - + + ######### validate + @verify_connected + def do_validate (self, line): + '''validates port(s) stream configuration\n''' + + self.stateless_client.cmd_validate_line(line) + + def help_events (self): self.do_events("-h") @@ -404,13 +402,10 @@ class TRexConsole(TRexGeneralCmd): print format_text("\n\nEvent log was cleared\n\n") # tui + @verify_connected def do_tui (self, line): '''Shows a graphical console\n''' - if not self.stateless_client.is_connected(): - print format_text("\nNot connected to server\n", 'bold') - return - self.do_verbose('off') trex_status.show_trex_status(self.stateless_client) -- cgit From 026f949fbafbb00fd7a21f3d84a632f5745003ea Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 6 Dec 2015 08:11:42 -0500 Subject: ZMQ bug - connect / disconnect fron another thread on pyhton (not safe !) fixed and more hardening --- .../trex_control_plane/client/trex_async_client.py | 82 ++++++++++++++++++---- .../client/trex_stateless_client.py | 31 ++++---- .../client_utils/jsonrpc_client.py | 4 +- .../trex_control_plane/console/trex_console.py | 1 + 4 files changed, 87 insertions(+), 31 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index 6793a4ca..e38c6ca7 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -159,39 +159,94 @@ class CTRexAsyncClient(): self.stats = TrexAsyncStatsManager() + self.connected = False + + # connects the async channel + def connect (self): + + if self.connected: + self.disconnect() self.tr = "tcp://{0}:{1}".format(self.server, self.port) - print "\nConnecting To ZMQ Publisher At {0}".format(self.tr) + print "\nConnecting To ZMQ Publisher On {0}".format(self.tr) + # Socket to talk to server + self.context = zmq.Context() + self.socket = self.context.socket(zmq.SUB) + + + # before running the thread - mark as active self.active = True - self.t = threading.Thread(target = self.run) + self.alive = False + self.t = threading.Thread(target = self._run) # kill this thread on exit and don't add it to the join list self.t.setDaemon(True) self.t.start() + self.connected = True - def run (self): + # wait for data streaming from the server + timeout = time.time() + 5 + while not self.alive: + time.sleep(0.01) + if time.time() > timeout: + self.disconnect() + return False, "*** [subscriber] - no data flow from server at : " + self.tr - # Socket to talk to server - self.context = zmq.Context() - self.socket = self.context.socket(zmq.SUB) + return True, "" + + + # disconnect + def disconnect (self): + if not self.connected: + return + + # signal that the context was destroyed (exit the thread loop) + self.context.term() + + # mark for join and join + self.active = False + self.t.join() + + # done + self.connected = False + + # thread function + def _run (self): + + # no data yet... + self.alive = False + # socket must be created on the same thread self.socket.connect(self.tr) self.socket.setsockopt(zmq.SUBSCRIBE, '') - self.socket.setsockopt(zmq.RCVTIMEO, 3000) + self.socket.setsockopt(zmq.RCVTIMEO, 5000) while self.active: try: line = self.socket.recv_string(); - self.stateless_client.on_async_alive() + if not self.alive: + self.stateless_client.on_async_alive() + self.alive = True + + # got a timeout - mark as not alive and retry except zmq.Again: - self.stateless_client.on_async_dead() + + if self.alive: + self.stateless_client.on_async_dead() + self.alive = False + continue + except zmq.ContextTerminated: + # outside thread signaled us to exit + self.alive = False + break + msg = json.loads(line) name = msg['name'] @@ -201,6 +256,10 @@ class CTRexAsyncClient(): self.__dispatch(name, type, data) + + # closing of socket must be from the same thread + self.socket.close(linger = 0) + def get_stats (self): return self.stats @@ -220,8 +279,3 @@ class CTRexAsyncClient(): else: pass - - def stop (self): - self.active = False - self.t.join() - diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 6082863e..3a9d74a9 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -445,14 +445,13 @@ class CTRexStatelessClient(object): self._server_version = None self.__err_log = None - self._async_client = CTRexAsyncClient(server, async_port, self) + self.async_client = CTRexAsyncClient(server, async_port, self) self.streams_db = CStreamsDB() - self.connected = False - self.events = [] + self.connected = False ################# events handler ###################### def add_event_log (self, msg, ev_type, show = False): @@ -515,7 +514,8 @@ class CTRexStatelessClient(object): self.ports[port_id].async_event_port_stopped() def async_event_server_stopped (self): - self.disconnect() + self.connected = False + def get_events (self): return self.events @@ -582,13 +582,19 @@ class CTRexStatelessClient(object): # connection sequence def connect(self): + # clear this flag self.connected = False - # connect + # connect sync channel rc, data = self.comm_link.connect() if not rc: return RC_ERR(data) + # connect async channel + rc, data = self.async_client.connect() + if not rc: + return RC_ERR(data) + # version rc, data = self.transmit("get_version") if not rc: @@ -634,24 +640,19 @@ class CTRexStatelessClient(object): def disconnect(self): - self.connected = False self.comm_link.disconnect() + self.async_client.disconnect() return RC_OK() def on_async_dead (self): - if self.is_connected(): + if self.connected: msg = 'lost connection to server' self.add_event_log(msg, 'local', True) - - self.disconnect() + self.connected = False def on_async_alive (self): - if not self.is_connected(): - msg = 'server connection restored' - self.add_event_log(msg, 'local', True) - - self.cmd_connect() + pass ########### cached queries (no server traffic) ########### @@ -675,7 +676,7 @@ class CTRexStatelessClient(object): return port_ids def get_stats_async (self): - return self._async_client.get_stats() + return self.async_client.get_stats() def get_connection_port (self): return self.comm_link.port diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index a5789c46..90d7f8e8 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -133,7 +133,7 @@ class JsonRpcClient(object): tries += 1 if tries > 10: self.disconnect() - return CmdResponse(False, "Failed to send message to server") + return CmdResponse(False, "*** [RPC] - Failed to send message to server") tries = 0 @@ -145,7 +145,7 @@ class JsonRpcClient(object): tries += 1 if tries > 10: self.disconnect() - return CmdResponse(False, "Failed to get server response") + return CmdResponse(False, "*** [RPC] - Failed to get server response") self.verbose_msg("Server Response:\n\n" + self.pretty_json(response) + "\n") diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 73f4f612..9d855f98 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -153,6 +153,7 @@ class TRexConsole(TRexGeneralCmd): self.__dict__[name] = getattr(self.trex_console, name) def postcmd(self, stop, line): + if self.stateless_client.is_connected(): self.prompt = "TRex > " else: -- cgit From 5011073de1cd9c2c0be2dbee3a3a6248afa7075b Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Sun, 6 Dec 2015 20:02:22 +0200 Subject: python API:fix no latency specified in tests --- .../trex_control_plane/client/trex_client.py | 30 ++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_client.py b/scripts/automation/trex_control_plane/client/trex_client.py index 160abdec..77b11c37 100755 --- a/scripts/automation/trex_control_plane/client/trex_client.py +++ b/scripts/automation/trex_control_plane/client/trex_client.py @@ -131,7 +131,9 @@ class CTRexClient(object): raise ValueError('d parameter must be integer, specifying how long TRex run, and must be larger than 30 secs.') trex_cmd_options.update( {'f' : f, 'd' : d} ) - + if not trex_cmd_options.get('l'): + self.result_obj.latency_checked = False + self.result_obj.clear_results() try: issue_time = time.time() @@ -767,6 +769,7 @@ class CTRexResult(object): """ self._history = deque(maxlen = max_history_size) self.clear_results() + self.latency_checked = True def __repr__(self): return ("Is valid history? {arg}\n".format( arg = self.is_valid_hist() ) + @@ -1032,18 +1035,19 @@ class CTRexResult(object): self._done_warmup = True # handle latency data - latency_pre = "trex-latency" - self._max_latency = self.get_last_value("{latency}.data".format(latency = latency_pre), ".*max-")#None # TBC - # support old typo - if self._max_latency is None: - latency_pre = "trex-latecny" - self._max_latency = self.get_last_value("{latency}.data".format(latency = latency_pre), ".*max-") - - self._avg_latency = self.get_last_value("{latency}.data".format(latency = latency_pre), "avg-")#None # TBC - self._avg_latency = CTRexResult.__avg_all_and_rename_keys(self._avg_latency) - - avg_win_latency_list = self.get_value_list("{latency}.data".format(latency = latency_pre), "avg-") - self._avg_window_latency = CTRexResult.__calc_latency_win_stats(avg_win_latency_list) + if self.latency_checked: + latency_pre = "trex-latency" + self._max_latency = self.get_last_value("{latency}.data".format(latency = latency_pre), ".*max-")#None # TBC + # support old typo + if self._max_latency is None: + latency_pre = "trex-latecny" + self._max_latency = self.get_last_value("{latency}.data".format(latency = latency_pre), ".*max-") + + self._avg_latency = self.get_last_value("{latency}.data".format(latency = latency_pre), "avg-")#None # TBC + self._avg_latency = CTRexResult.__avg_all_and_rename_keys(self._avg_latency) + + avg_win_latency_list = self.get_value_list("{latency}.data".format(latency = latency_pre), "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") rx_pkts = CTRexResult.__get_value_by_path(latest_dump, "trex-global.data.m_total_rx_pkts") -- cgit From 503c10b024aa2ed6d4d8dc7fb5debf4a64bd9b1e Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Mon, 7 Dec 2015 08:29:17 +0200 Subject: Re-designed the statistic building model based on agreed diagram. WORKING: all polling stats --- .../trex_control_plane/client/trex_async_client.py | 8 +- .../client/trex_stateless_client.py | 139 +++++++---- .../trex_control_plane/common/trex_stats.py | 261 +++++++++++---------- .../trex_control_plane/console/trex_status.py | 2 +- 4 files changed, 233 insertions(+), 177 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index a2bb4752..8b274134 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -51,7 +51,7 @@ class CTRexAsyncStats(object): self.ref_point = self.current - def get(self, field, format = False, suffix = ""): + def get(self, field, format=False, suffix=""): if not field in self.current: return "N/A" @@ -61,8 +61,7 @@ class CTRexAsyncStats(object): else: return self.format_num(self.current[field], suffix) - - def get_rel (self, field, format = False, suffix = ""): + def get_rel (self, field, format=False, suffix=""): if not field in self.current: return "N/A" @@ -204,7 +203,8 @@ class CTRexAsyncClient(): def __dispatch (self, name, type, data): # stats if name == "trex-global": - self.stats.update(data) + # self.stats.update(data) + self.stateless_client.handle_async_stats_update(data) # events elif name == "trex-event": self.stateless_client.handle_async_event(type, data) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 7be7392e..e02620a6 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -19,6 +19,7 @@ from common import trex_stats from client_utils import parsing_opts, text_tables import time import datetime +import re from trex_async_client import CTRexAsyncClient @@ -165,6 +166,7 @@ class Port(object): self.driver = driver self.speed = speed self.streams = {} + self.port_stats = trex_stats.CPortStats(self) def err(self, msg): return RC_ERR("port {0} : {1}".format(self.port_id, msg)) @@ -189,7 +191,6 @@ class Port(object): else: return self.err(rc.data) - # release the port def release(self): params = {"port_id": self.port_id, @@ -409,6 +410,16 @@ class Port(object): def get_port_state_name(self): return self.STATES_MAP.get(self.state, "Unknown") + ################# stats handler ###################### + def generate_port_stats(self): + return self.port_stats.generate_stats() + pass + + def generate_port_status(self): + return {"port-type": self.driver, + "maximum": "{speed} Gb/s".format(speed=self.speed), + "port-status": self.get_port_state_name() + } ################# events handler ###################### def async_event_port_stopped (self): @@ -424,22 +435,24 @@ class CTRexStatelessClient(object): self.system_info = None self.comm_link = CTRexStatelessClient.CCommLink(server, sync_port, virtual) self.verbose = False - self.ports = [] - self._conn_handler = {} - self._active_ports = set() - self._system_info = None - self._server_version = None + self.ports = {} + # self._conn_handler = {} + # self._active_ports = set() + self._connection_info = {"server": server, + "sync_port": sync_port, + "async_port": async_port} + self.system_info = {} + self.server_version = {} self.__err_log = None self._async_client = CTRexAsyncClient(server, async_port, self) self.streams_db = CStreamsDB() - self.info_and_stats = trex_stats.CTRexInformationCenter(self.user, - {"server": server, - "sync_port": sync_port, - "async_port": async_port}, - self.ports, - self.get_stats_async()) + self.global_stats = trex_stats.CGlobalStats(self._connection_info, + self.server_version, + self.ports) + self.stats_generator = trex_stats.CTRexStatsGenerator(self.global_stats, + self.ports) self.connected = False @@ -447,6 +460,33 @@ class CTRexStatelessClient(object): ################# events handler ###################### + def handle_async_stats_update(self, dump_data): + global_stats = {} + port_stats = {} + + # filter the values per port and general + for key, value in dump_data.iteritems(): + # match a pattern of ports + m = re.search('(.*)\-([0-8])', key) + if m: + port_id = int(m.group(2)) + field_name = m.group(1) + if self.ports.has_key(port_id): + if not port_id in port_stats: + port_stats[port_id] = {} + port_stats[port_id][field_name] = value + else: + continue + else: + # no port match - general stats + global_stats[key] = value + + # update the general object with the snapshot + self.global_stats.update(global_stats) + # update all ports + for port_id, data in port_stats.iteritems(): + self.ports[port_id].port_stats.update(data) + def handle_async_event (self, type, data): # DP stopped @@ -556,7 +596,7 @@ class CTRexStatelessClient(object): return RC_ERR(data) self.server_version = data - self.info_and_stats.server_version = data + self.global_stats.server_version = data # cache system info # self.get_system_info(refresh=True) @@ -564,7 +604,6 @@ class CTRexStatelessClient(object): if not rc: return RC_ERR(data) self.system_info = data - self.info_and_stats.system_info = data # cache supported commands rc, data = self.transmit("get_supported_cmds") @@ -577,7 +616,7 @@ class CTRexStatelessClient(object): for port_id in xrange(self.get_port_count()): speed = self.system_info['ports'][port_id]['speed'] driver = self.system_info['ports'][port_id]['driver'] - self.ports.append(Port(port_id, speed, driver, self.user, self.transmit)) + self.ports[port_id] = Port(port_id, speed, driver, self.user, self.transmit) # acquire all ports rc = self.acquire() @@ -634,11 +673,14 @@ class CTRexStatelessClient(object): return self.comm_link.server def get_acquired_ports(self): - return [port.port_id for port in self.ports if port.is_acquired()] - + return [port_id + for port_id, port_obj in self.ports.iteritems() + if port_obj.is_acquired()] def get_active_ports(self): - return [port.port_id for port in self.ports if port.is_active()] + return [port_id + for port_id, port_obj in self.ports.iteritems() + if port_obj.is_active()] def set_verbose(self, mode): self.comm_link.set_verbose(mode) @@ -893,7 +935,7 @@ class CTRexStatelessClient(object): # update cmd def cmd_update (self, port_id_list, mult): - # find the relveant ports + # find the relevant ports active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) if not active_ports: @@ -909,15 +951,14 @@ class CTRexStatelessClient(object): return RC_OK() def cmd_clear(self, port_id_list): - self.info_and_stats.clear(port_id_list) + # self.info_and_stats.clear(port_id_list) return RC_OK() - # pause cmd def cmd_pause (self, port_id_list): - # find the relveant ports + # find the relevant ports active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) if not active_ports: @@ -932,19 +973,6 @@ class CTRexStatelessClient(object): return RC_OK() - def cmd_pause_line (self, line): - '''Pause active traffic in specified ports on TRex\n''' - parser = parsing_opts.gen_parser(self, - "pause", - self.cmd_stop_line.__doc__, - parsing_opts.PORT_LIST_WITH_ALL) - - opts = parser.parse_args(line.split()) - if opts is None: - return RC_ERR("bad command line parameters") - - return self.cmd_pause(opts.ports) - # resume cmd def cmd_resume (self, port_id_list): @@ -965,20 +993,6 @@ class CTRexStatelessClient(object): return RC_OK() - def cmd_resume_line (self, line): - '''Resume active traffic in specified ports on TRex\n''' - parser = parsing_opts.gen_parser(self, - "resume", - self.cmd_stop_line.__doc__, - parsing_opts.PORT_LIST_WITH_ALL) - - opts = parser.parse_args(line.split()) - if opts is None: - return RC_ERR("bad command line parameters") - - return self.cmd_resume(opts.ports) - - # start cmd def cmd_start (self, port_id_list, stream_list, mult, force, duration): @@ -1020,7 +1034,7 @@ class CTRexStatelessClient(object): stats_obj = {} for stats_type in stats_opts: - stats_obj.update(self.info_and_stats.generate_single_statistic(port_id_list, stats_type)) + stats_obj.update(self.stats_generator.generate_single_statistic(port_id_list, stats_type)) return stats_obj @@ -1066,6 +1080,19 @@ class CTRexStatelessClient(object): return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force, opts.duration) + def cmd_resume_line (self, line): + '''Resume active traffic in specified ports on TRex\n''' + parser = parsing_opts.gen_parser(self, + "resume", + self.cmd_stop_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + + opts = parser.parse_args(line.split()) + if opts is None: + return RC_ERR("bad command line parameters") + + return self.cmd_resume(opts.ports) + def cmd_stop_line (self, line): '''Stop active traffic in specified ports on TRex\n''' parser = parsing_opts.gen_parser(self, @@ -1079,6 +1106,18 @@ class CTRexStatelessClient(object): return self.cmd_stop(opts.ports) + def cmd_pause_line (self, line): + '''Pause active traffic in specified ports on TRex\n''' + parser = parsing_opts.gen_parser(self, + "pause", + self.cmd_stop_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL) + + opts = parser.parse_args(line.split()) + if opts is None: + return RC_ERR("bad command line parameters") + + return self.cmd_pause(opts.ports) def cmd_update_line (self, line): '''Update port(s) speed currently active\n''' diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py index ec455730..1f9d59e3 100755 --- a/scripts/automation/trex_control_plane/common/trex_stats.py +++ b/scripts/automation/trex_control_plane/common/trex_stats.py @@ -4,6 +4,8 @@ from client_utils import text_tables from common.text_opts import format_text from client.trex_async_client import CTRexAsyncStats import copy +import datetime +import re GLOBAL_STATS = 'g' PORT_STATS = 'p' @@ -12,28 +14,15 @@ ALL_STATS_OPTS = {GLOBAL_STATS, PORT_STATS, PORT_STATUS} ExportableStats = namedtuple('ExportableStats', ['raw_data', 'text_table']) -class CTRexInformationCenter(object): +class CTRexStatsGenerator(object): + """ + This object is responsible of generating stats from objects maintained at + CTRexStatelessClient and the ports. + """ - def __init__(self, username, connection_info, ports_ref, async_stats_ref): - self.user = username - self.connection_info = connection_info - self.server_version = None - self.system_info = None - self._ports = ports_ref - self._async_stats = async_stats_ref - - # def __getitem__(self, item): - # stats_obj = getattr(self, item) - # if stats_obj: - # return stats_obj.get_stats() - # else: - # return None - - def clear(self, port_id_list): - self._async_stats.get_general_stats().clear() - for port_id in port_id_list: - self._async_stats.get_port_stats(port_id).clear() - pass + def __init__(self, global_stats_ref, ports_dict_ref): + self._global_stats = global_stats_ref + self._ports_dict = ports_dict_ref def generate_single_statistic(self, port_id_list, statistic_type): if statistic_type == GLOBAL_STATS: @@ -48,32 +37,17 @@ class CTRexInformationCenter(object): return {} def _generate_global_stats(self): - stats_obj = self._async_stats.get_general_stats() - return_stats_data = \ - OrderedDict([("connection", "{host}, Port {port}".format(host=self.connection_info.get("server"), - port=self.connection_info.get("sync_port"))), - ("version", "{ver}, UUID: {uuid}".format(ver=self.server_version.get("version", "N/A"), - uuid="N/A")), - ("cpu_util", "{0}%".format(stats_obj.get("m_cpu_util"))), - ("total_tx", stats_obj.get("m_tx_bps", format=True, suffix="b/sec")), - ("total_rx", stats_obj.get("m_rx_bps", format=True, suffix="b/sec")), - ("total_pps", stats_obj.format_num(stats_obj.get("m_tx_pps") + stats_obj.get("m_rx_pps"), - suffix="pkt/sec")), - ("total_streams", sum([len(port.streams) - for port in self._ports])), - ("active_ports", sum([port.is_active() - for port in self._ports])) - ] - ) + # stats_obj = self._async_stats.get_general_stats() + stats_data = self._global_stats.generate_stats() # build table representation stats_table = text_tables.TRexTextInfo() stats_table.set_cols_align(["l", "l"]) stats_table.add_rows([[k.replace("_", " ").title(), v] - for k, v in return_stats_data.iteritems()], + for k, v in stats_data.iteritems()], header=False) - return {"global_statistics": ExportableStats(return_stats_data, stats_table)} + return {"global_statistics": ExportableStats(stats_data, stats_table)} def _generate_port_stats(self, port_id_list): relevant_ports = self.__get_relevant_ports(port_id_list) @@ -94,41 +68,11 @@ class CTRexInformationCenter(object): for port_obj in relevant_ports: # fetch port data - port_stats = self._async_stats.get_port_stats(port_obj.port_id) - - owner = self.user - active = "YES" if port_obj.is_active() else "NO" - tx_bytes = port_stats.get_rel("obytes", format = True, suffix = "B") - rx_bytes = port_stats.get_rel("ibytes", format = True, suffix = "B") - tx_pkts = port_stats.get_rel("opackets", format = True, suffix = "pkts") - rx_pkts = port_stats.get_rel("ipackets", format = True, suffix = "pkts") - tx_errors = port_stats.get_rel("oerrors", format = True) - rx_errors = port_stats.get_rel("ierrors", format = True) - tx_bw = port_stats.get("m_total_tx_bps", format = True, suffix = "bps") - rx_bw = port_stats.get("m_total_rx_bps", format = True, suffix = "bps") + port_stats = port_obj.generate_port_stats() # populate to data structures - return_stats_data[port_obj.port_id] = {"owner": owner, - "active": active, - "tx-bytes": tx_bytes, - "rx-bytes": rx_bytes, - "tx-pkts": tx_pkts, - "rx-pkts": rx_pkts, - "tx-errors": tx_errors, - "rx-errors": rx_errors, - "Tx-BW": tx_bw, - "Rx-BW": rx_bw - } - per_field_stats["owner"].append(owner) - per_field_stats["active"].append(active) - per_field_stats["tx-bytes"].append(tx_bytes) - per_field_stats["rx-bytes"].append(rx_bytes) - per_field_stats["tx-pkts"].append(tx_pkts) - per_field_stats["rx-pkts"].append(rx_pkts) - per_field_stats["tx-errors"].append(tx_errors) - per_field_stats["rx-errors"].append(rx_errors) - per_field_stats["tx-BW"].append(tx_bw) - per_field_stats["rx-BW"].append(rx_bw) + return_stats_data[port_obj.port_id] = port_stats + self.__update_per_field_dict(port_stats, per_field_stats) stats_table = text_tables.TRexTextTable() stats_table.set_cols_align(["l"] + ["r"]*len(relevant_ports)) @@ -152,21 +96,13 @@ class CTRexInformationCenter(object): for port_obj in relevant_ports: # fetch port data - port_stats = self._async_stats.get_port_stats(port_obj.port_id) - - - port_type = port_obj.driver - maximum = "{speed} Gb/s".format(speed=port_obj.speed)#CTRexAsyncStats.format_num(port_obj.get_speed_bps(), suffix="bps") - port_status = port_obj.get_port_state_name() + # port_stats = self._async_stats.get_port_stats(port_obj.port_id) + port_status = port_obj.generate_port_status() # populate to data structures - return_stats_data[port_obj.port_id] = {"port-type": port_type, - "maximum": maximum, - "port-status": port_status, - } - per_field_status["port-type"].append(port_type) - per_field_status["maximum"].append(maximum) - per_field_status["port-status"].append(port_status) + return_stats_data[port_obj.port_id] = port_status + + self.__update_per_field_dict(port_status, per_field_status) stats_table = text_tables.TRexTextTable() stats_table.set_cols_align(["l"] + ["c"]*len(relevant_ports)) @@ -181,7 +117,7 @@ class CTRexInformationCenter(object): def __get_relevant_ports(self, port_id_list): # fetch owned ports ports = [port_obj - for port_obj in self._ports + for _, port_obj in self._ports_dict.iteritems() if port_obj.is_acquired() and port_obj.port_id in port_id_list] # display only the first FOUR options, by design if len(ports) > 4: @@ -189,61 +125,142 @@ class CTRexInformationCenter(object): ports = ports[:4] return ports + def __update_per_field_dict(self, dict_src_data, dict_dest_ref): + for key, val in dict_src_data.iteritems(): + if key in dict_dest_ref: + dict_dest_ref[key].append(val) -class CTRexStatsManager(object): - def __init__(self, *args): - for stat_type in args: - # register stat handler for each stats type - setattr(self, stat_type, CTRexStatsManager.CSingleStatsHandler()) +class CTRexStats(object): + """ This is an abstract class to represent a stats object """ + + def __init__(self): + self.reference_stats = None + self.latest_stats = {} + self.last_update_ts = datetime.datetime.now() + def __getitem__(self, item): - stats_obj = getattr(self, item) - if stats_obj: - return stats_obj.get_stats() - else: - return None + # override this to allow quick and clean access to fields + if not item in self.latest_stats: + return "N/A" + + # item must exist + m = re.search('_(([a-z])ps)$', item) + if m: + # this is a non-relative item + unit = m.group(2) + if unit == "b": + return self.get(item, format=True, suffix="b/sec") + elif unit == "p": + return self.get(item, format=True, suffix="pkt/sec") + else: + return self.get(item, format=True, suffix=m.group(1)) + + m = re.search('^[i|o](a-z+)$', item) + if m: + # this is a non-relative item + type = m.group(1) + if type == "bytes": + return self.get_rel(item, format=True, suffix="B") + elif type == "packets": + return self.get_rel(item, format=True, suffix="pkts") + else: + # do not format with suffix + return self.get_rel(item, format=True) - class CSingleStatsHandler(object): + # can't match to any known pattern, return N/A + return "N/A" - def __init__(self): - self._stats = {} + @staticmethod + def format_num(size, suffix = ""): + for unit in ['','K','M','G','T','P']: + if abs(size) < 1000.0: + return "%3.2f %s%s" % (size, unit, suffix) + size /= 1000.0 + return "NaN" - def update(self, obj_id, stats_obj): - assert isinstance(stats_obj, CTRexStats) - self._stats[obj_id] = stats_obj + def generate_stats(self): + # must be implemented by designated classes (such as port/ global stats) + raise NotImplementedError() - def get_stats(self, obj_id=None): - if obj_id: - return copy.copy(self._stats.pop(obj_id)) - else: - return copy.copy(self._stats) + def update(self, snapshot): + # update + self.last_update_ts = datetime.datetime.now() + self.latest_stats = snapshot -class CTRexStats(object): - def __init__(self, **kwargs): - for k, v in kwargs.items(): - setattr(self, k, v) + if self.reference_stats == None: + self.reference_stats = self.latest_stats + + def clear_stats(self): + self.reference_stats = self.latest_stats + + def get(self, field, format=False, suffix=""): + if not field in self.latest_stats: + return "N/A" + if not format: + return self.latest_stats[field] + else: + return self.format_num(self.latest_stats[field], suffix) + + def get_rel(self, field, format=False, suffix=""): + if not field in self.latest_stats: + return "N/A" + if not format: + return (self.latest_stats[field] - self.reference_stats[field]) + else: + return self.format_num(self.latest_stats[field] - self.reference_stats[field], suffix) class CGlobalStats(CTRexStats): - def __init__(self, **kwargs): - super(CGlobalStats, self).__init__(kwargs) - pass + pass + + def __init__(self, connection_info, server_version, ports_dict_ref): + super(CGlobalStats, self).__init__() + self.connection_info = connection_info + self.server_version = server_version + self._ports_dict = ports_dict_ref + def generate_stats(self): + return OrderedDict([("connection", "{host}, Port {port}".format(host=self.connection_info.get("server"), + port=self.connection_info.get("sync_port"))), + ("version", "{ver}, UUID: {uuid}".format(ver=self.server_version.get("version", "N/A"), + uuid="N/A")), + ("cpu_util", "{0}%".format(self.get("m_cpu_util"))), + ("total_tx", self.get("m_tx_bps", format=True, suffix="b/sec")), + ("total_rx", self.get("m_rx_bps", format=True, suffix="b/sec")), + ("total_pps", self.format_num(self.get("m_tx_pps") + self.get("m_rx_pps"), + suffix="pkt/sec")), + ("total_streams", sum([len(port_obj.streams) + for _, port_obj in self._ports_dict.iteritems()])), + ("active_ports", sum([port_obj.is_active() + for _, port_obj in self._ports_dict.iteritems()])) + ] + ) class CPortStats(CTRexStats): - def __init__(self, **kwargs): - super(CPortStats, self).__init__(kwargs) - pass + pass + def __init__(self, port_obj): + super(CPortStats, self).__init__() + self._port_obj = port_obj + + def generate_stats(self): + return {"owner": self._port_obj.user, + "active": "YES" if self._port_obj.is_active() else "NO", + "tx-bytes": self.get_rel("obytes", format = True, suffix = "B"), + "rx-bytes": self.get_rel("ibytes", format = True, suffix = "B"), + "tx-pkts": self.get_rel("opackets", format = True, suffix = "pkts"), + "rx-pkts": self.get_rel("ipackets", format = True, suffix = "pkts"), + "tx-errors": self.get_rel("oerrors", format = True), + "rx-errors": self.get_rel("ierrors", format = True), + "tx-BW": self.get("m_total_tx_bps", format = True, suffix = "bps"), + "rx-BW": self.get("m_total_rx_bps", format = True, suffix = "bps") + } -class CStreamStats(CTRexStats): - def __init__(self, **kwargs): - super(CStreamStats, self).__init__(kwargs) - pass if __name__ == "__main__": diff --git a/scripts/automation/trex_control_plane/console/trex_status.py b/scripts/automation/trex_control_plane/console/trex_status.py index 10ac75c9..cdf3fb69 100644 --- a/scripts/automation/trex_control_plane/console/trex_status.py +++ b/scripts/automation/trex_control_plane/console/trex_status.py @@ -494,7 +494,7 @@ class CTRexStatus(): self.stats_panel.panel.top() self.stats_panel.draw() - panel.update_panels(); + panel.update_panels() self.stdscr.refresh() sleep(0.01) -- cgit From 1895d21485621c3428d045fa0f5b9daf165c8260 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Mon, 7 Dec 2015 08:35:46 +0200 Subject: clear command now works as well.. --- .../trex_control_plane/client/trex_stateless_client.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index e02620a6..748817da 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -421,6 +421,9 @@ class Port(object): "port-status": self.get_port_state_name() } + def clear_stats(self): + return self.port_stats.clear_stats() + ################# events handler ###################### def async_event_port_stopped (self): self.state = self.STATE_STREAMS @@ -951,10 +954,11 @@ class CTRexStatelessClient(object): return RC_OK() def cmd_clear(self, port_id_list): - # self.info_and_stats.clear(port_id_list) + for port_id in port_id_list: + self.ports[port_id].clear_stats() + self.global_stats.clear_stats() return RC_OK() - # pause cmd def cmd_pause (self, port_id_list): @@ -962,7 +966,7 @@ class CTRexStatelessClient(object): active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) if not active_ports: - msg = "No active traffic on porvided ports" + msg = "No active traffic on provided ports" print format_text(msg, 'bold') return RC_ERR(msg) -- cgit From b420c37087df630bcc0f97baccd073b838517b46 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 7 Dec 2015 03:40:55 -0500 Subject: some validate info --- .../client/trex_stateless_client.py | 66 ++++++++++++++-------- .../trex_control_plane/common/text_opts.py | 11 ++++ 2 files changed, 53 insertions(+), 24 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 3a9d74a9..9b421acb 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -49,10 +49,10 @@ class RC: return not self.good() def data (self): - return all([x.data if x.rc else "" for x in self.rc_list]) + return [x.data if x.rc else "" for x in self.rc_list] def err (self): - return all([x.data if not x.rc else "" for x in self.rc_list]) + return [x.data if not x.rc else "" for x in self.rc_list] def annotate (self, desc = None): if desc: @@ -73,8 +73,8 @@ class RC: print format_text("[SUCCESS]\n", 'green', 'bold') -def RC_OK(): - return RC(True, "") +def RC_OK(data = None): + return RC(True, data) def RC_ERR (err): return RC(False, err) @@ -158,12 +158,13 @@ class Port(object): self.driver = driver self.speed = speed self.streams = {} + self.profile = None def err(self, msg): return RC_ERR("port {0} : {1}".format(self.port_id, msg)) - def ok(self): - return RC_OK() + def ok(self, data = None): + return RC_OK(data) def get_speed_bps (self): return (self.speed * 1000 * 1000 * 1000) @@ -416,6 +417,13 @@ class Port(object): def validate (self): + + if (self.state == self.STATE_DOWN): + return self.err("port is down") + + if (self.state == self.STATE_IDLE): + return self.err("no streams attached to port") + params = {"handler": self.handler, "port_id": self.port_id} @@ -423,8 +431,13 @@ class Port(object): if not rc: return self.err(data) + self.profile = data + return self.ok() + def get_profile (self): + return self.profile + ################# events handler ###################### def async_event_port_stopped (self): self.state = self.STATE_STREAMS @@ -942,7 +955,7 @@ class CTRexStatelessClient(object): active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) if not active_ports: - msg = "No active traffic on porvided ports" + msg = "No active traffic on provided ports" print format_text(msg, 'bold') return RC_ERR(msg) @@ -966,10 +979,8 @@ class CTRexStatelessClient(object): rc = self.update_traffic(mult, active_ports) rc.annotate("Updating traffic on port(s) {0}:".format(port_id_list)) - if rc.bad(): - return rc - return RC_OK() + return rc # pause cmd @@ -985,10 +996,8 @@ class CTRexStatelessClient(object): rc = self.pause_traffic(active_ports) rc.annotate("Pausing traffic on port(s) {0}:".format(port_id_list)) - if rc.bad(): - return rc - return RC_OK() + return rc # resume cmd @@ -1004,10 +1013,8 @@ class CTRexStatelessClient(object): rc = self.resume_traffic(active_ports) rc.annotate("Resume traffic on port(s) {0}:".format(port_id_list)) - if rc.bad(): - return rc - return RC_OK() + return rc # start cmd @@ -1040,19 +1047,15 @@ class CTRexStatelessClient(object): # finally, start the traffic rc = self.start_traffic(mult, duration, port_id_list) rc.annotate("Starting traffic on port(s) {0}:".format(port_id_list)) - if rc.bad(): - return rc - return RC_OK() + return rc + def cmd_validate (self, port_id_list): rc = self.validate(port_id_list) rc.annotate("Validating streams on port(s) {0}:".format(port_id_list)) - if rc.bad(): - return rc - - return RC_OK() + return rc ############## High Level API With Parser ################ @timing @@ -1181,7 +1184,22 @@ class CTRexStatelessClient(object): if opts is None: return RC_ERR("bad command line paramters") - return self.cmd_validate(opts.ports) + rc = self.cmd_validate(opts.ports) + if rc.bad(): + return rc + + # for each port - print the profile + port_id_list = self.__ports(opts.ports) + for port_id in port_id_list: + port = self.ports[port_id] + rate = port.get_profile()['rate'] + print format_text("Port {0}:\n".format(port_id), 'underline', 'bold') + print "Profile max BPS (m = 1): {:>15}".format(format_num(rate['max_bps'], suffix = "bps")) + print "Profile max PPS (m = 1): {:>15}".format(format_num(rate['max_pps'], suffix = "pps")) + print "Profile line util. (m = 1): {:>15}".format(format_percentage(rate['max_line_util'] * 100)) + print "\n" + + return rc def cmd_exit_line (self, line): diff --git a/scripts/automation/trex_control_plane/common/text_opts.py b/scripts/automation/trex_control_plane/common/text_opts.py index 06c2c056..69c76674 100755 --- a/scripts/automation/trex_control_plane/common/text_opts.py +++ b/scripts/automation/trex_control_plane/common/text_opts.py @@ -19,6 +19,17 @@ TEXT_CODES = {'bold': {'start': '\x1b[1m', 'end': '\x1b[24m'}} +def format_num (size, suffix = ""): + for unit in ['','K','M','G','T','P']: + if abs(size) < 1000.0: + return "%3.2f %s%s" % (size, unit, suffix) + size /= 1000.0 + + return "NaN" + +def format_percentage (size): + return "%0.2f %%" % (size) + def bold(text): return text_attribute(text, 'bold') -- cgit From 0fc30adae2fc5708baef74d36e97a174b078f332 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 7 Dec 2015 09:08:02 -0500 Subject: added 'dry' option to start command this enables showing a profile map before starting --- .../client/trex_stateless_client.py | 106 +++++++++++++++------ .../trex_control_plane/common/text_opts.py | 33 +++++++ .../trex_control_plane/console/parsing_opts.py | 9 +- 3 files changed, 120 insertions(+), 28 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 9b421acb..ca2ad0bb 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -81,6 +81,22 @@ def RC_ERR (err): LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) +########## utlity ############ +def mult_to_factor (mult, max_bps, max_pps, line_util): + if mult['type'] == 'raw': + return mult['value'] + + if mult['type'] == 'bps': + return mult['value'] / max_bps + + if mult['type'] == 'pps': + return mult['value'] / max_pps + + if mult['type'] == 'percentage': + return mult['value'] / line_util + + + # describes a stream DB class CStreamsDB(object): @@ -438,6 +454,44 @@ class Port(object): def get_profile (self): return self.profile + + def print_profile (self, mult, duration): + if not self.get_profile(): + return + + rate = self.get_profile()['rate'] + graph = self.get_profile()['graph'] + + print format_text("Profile Map Per Port\n", 'underline', 'bold') + + factor = mult_to_factor(mult, rate['max_bps'], rate['max_pps'], rate['max_line_util']) + + print "Profile max BPS (base / req): {:^12} / {:^12}".format(format_num(rate['max_bps'], suffix = "bps"), + format_num(rate['max_bps'] * factor, suffix = "bps")) + + print "Profile max PPS (base / req): {:^12} / {:^12}".format(format_num(rate['max_pps'], suffix = "pps"), + format_num(rate['max_pps'] * factor, suffix = "pps"),) + + print "Profile line util. (base / req): {:^12} / {:^12}".format(format_percentage(rate['max_line_util'] * 100), + format_percentage(rate['max_line_util'] * factor * 100)) + + + # duration + exp_time_base_sec = graph['expected_duration'] / (1000 * 1000) + exp_time_factor_sec = exp_time_base_sec / factor + + # user configured a duration + if duration > 0: + if exp_time_factor_sec > 0: + exp_time_factor_sec = min(exp_time_factor_sec, duration) + else: + exp_time_factor_sec = duration + + + print "Duration (base / req): {:^12} / {:^12}".format(format_time(exp_time_base_sec), + format_time(exp_time_factor_sec)) + print "\n" + ################# events handler ###################### def async_event_port_stopped (self): self.state = self.STATE_STREAMS @@ -549,13 +603,8 @@ class CTRexStatelessClient(object): return ret delta = time.time() - time1 + print format_time(delta) + "\n" - for unit in ['sec','ms','usec']: - - if delta > 1.0: - print '{:,.2f} [{:}]\n'.format(delta, unit) - break - delta *= 1000.0 return ret return wrap @@ -1018,7 +1067,7 @@ class CTRexStatelessClient(object): # start cmd - def cmd_start (self, port_id_list, stream_list, mult, force, duration): + def cmd_start (self, port_id_list, stream_list, mult, force, duration, dry): active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) @@ -1044,11 +1093,23 @@ class CTRexStatelessClient(object): if rc.bad(): return rc - # finally, start the traffic - rc = self.start_traffic(mult, duration, port_id_list) - rc.annotate("Starting traffic on port(s) {0}:".format(port_id_list)) + # when not on dry - start the traffic , otherwise validate only + if not dry: + rc = self.start_traffic(mult, duration, port_id_list) + rc.annotate("Starting traffic on port(s) {0}:".format(port_id_list)) - return rc + return rc + else: + rc = self.validate(port_id_list) + rc.annotate("Validating traffic profile on port(s) {0}:".format(port_id_list)) + + if rc.bad(): + return rc + + # show a profile on one port for illustration + self.ports[port_id_list[0]].print_profile(mult, duration) + + return rc @@ -1070,7 +1131,8 @@ class CTRexStatelessClient(object): parsing_opts.FORCE, parsing_opts.STREAM_FROM_PATH_OR_FILE, parsing_opts.DURATION, - parsing_opts.MULTIPLIER_STRICT) + parsing_opts.MULTIPLIER_STRICT, + parsing_opts.DRY_RUN) opts = parser.parse_args(line.split()) @@ -1078,6 +1140,9 @@ class CTRexStatelessClient(object): return RC_ERR("bad command line paramters") + if opts.dry: + print format_text("\n*** DRY RUN ***", 'bold') + if opts.db: stream_list = self.stream_db.get_stream_pack(opts.db) rc = RC(stream_list != None) @@ -1099,7 +1164,7 @@ class CTRexStatelessClient(object): # if total was set - divide it between the ports opts.mult['value'] = opts.mult['value'] / len(opts.ports) - return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force, opts.duration) + return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force, opts.duration, opts.dry) @timing def cmd_stop_line (self, line): @@ -1185,20 +1250,6 @@ class CTRexStatelessClient(object): return RC_ERR("bad command line paramters") rc = self.cmd_validate(opts.ports) - if rc.bad(): - return rc - - # for each port - print the profile - port_id_list = self.__ports(opts.ports) - for port_id in port_id_list: - port = self.ports[port_id] - rate = port.get_profile()['rate'] - print format_text("Port {0}:\n".format(port_id), 'underline', 'bold') - print "Profile max BPS (m = 1): {:>15}".format(format_num(rate['max_bps'], suffix = "bps")) - print "Profile max PPS (m = 1): {:>15}".format(format_num(rate['max_pps'], suffix = "pps")) - print "Profile line util. (m = 1): {:>15}".format(format_percentage(rate['max_line_util'] * 100)) - print "\n" - return rc @@ -1277,6 +1328,7 @@ class CTRexStatelessClient(object): return True + ################################# # ------ private classes ------ # class CCommLink(object): diff --git a/scripts/automation/trex_control_plane/common/text_opts.py b/scripts/automation/trex_control_plane/common/text_opts.py index 69c76674..5a86149c 100755 --- a/scripts/automation/trex_control_plane/common/text_opts.py +++ b/scripts/automation/trex_control_plane/common/text_opts.py @@ -27,6 +27,39 @@ def format_num (size, suffix = ""): return "NaN" +def format_time (t_sec): + if t_sec < 0: + return "infinite" + + if t_sec < 1: + # low numbers + for unit in ['ms', 'usec', 'ns']: + t_sec *= 1000.0 + if t_sec >= 1.0: + return '{:,.2f} [{:}]'.format(t_sec, unit) + + return "NaN" + + else: + # seconds + if t_sec < 60.0: + return '{:,.2f} [{:}]'.format(t_sec, 'sec') + + # minutes + t_sec /= 60.0 + if t_sec < 60.0: + return '{:,.2f} [{:}]'.format(t_sec, 'minutes') + + # hours + t_sec /= 60.0 + if t_sec < 24.0: + return '{:,.2f} [{:}]'.format(t_sec, 'hours') + + # days + t_sec /= 24.0 + return '{:,.2f} [{:}]'.format(t_sec, 'days') + + def format_percentage (size): return "%0.2f %%" % (size) diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index d7bf583a..57ff06e0 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -20,7 +20,8 @@ SERVER_IP = 8 STREAM_FROM_PATH_OR_FILE = 9 DURATION = 10 FORCE = 11 -TOTAL = 12 +DRY_RUN = 12 +TOTAL = 13 # list of ArgumentGroup types MUTEX = 1 @@ -198,6 +199,12 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'metavar': 'SERVER', 'help': "server IP"}), + DRY_RUN: ArgumentPack(['-n', '--dry'], + {'action': 'store_true', + 'dest': 'dry', + 'default': False, + 'help': "Dry run - no traffic will be injected"}), + # advanced options PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST, ALL_PORTS], -- cgit From 01af153cf391d963a64cdd9b0d29226aca8b63b4 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 7 Dec 2015 10:53:57 -0500 Subject: removed duplications --- .../client/trex_stateless_client.py | 31 ---------------------- 1 file changed, 31 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 2925e7eb..0c6d9855 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -1374,37 +1374,6 @@ class CTRexStatelessClient(object): - - @timing - def cmd_pause_line (self, line): - '''Pause active traffic in specified ports on TRex\n''' - parser = parsing_opts.gen_parser(self, - "pause", - self.cmd_stop_line.__doc__, - parsing_opts.PORT_LIST_WITH_ALL) - - opts = parser.parse_args(line.split()) - if opts is None: - return RC_ERR("bad command line paramters") - - return self.cmd_pause(opts.ports) - - - @timing - def cmd_resume_line (self, line): - '''Resume active traffic in specified ports on TRex\n''' - parser = parsing_opts.gen_parser(self, - "resume", - self.cmd_stop_line.__doc__, - parsing_opts.PORT_LIST_WITH_ALL) - - opts = parser.parse_args(line.split()) - if opts is None: - return RC_ERR("bad command line paramters") - - return self.cmd_resume(opts.ports) - - @timing def cmd_validate_line (self, line): '''validates port(s) stream configuration\n''' -- cgit From d2cd7b153eef887cbf76ada54149e67bc1057d1e Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Tue, 8 Dec 2015 06:38:42 +0200 Subject: Fix to documentation of python function generate_run_cmd --- scripts/automation/trex_control_plane/server/trex_server.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/server/trex_server.py b/scripts/automation/trex_control_plane/server/trex_server.py index 7dee89e9..b1ce54e1 100755 --- a/scripts/automation/trex_control_plane/server/trex_server.py +++ b/scripts/automation/trex_control_plane/server/trex_server.py @@ -340,11 +340,12 @@ class CTRexServer(object): Parameters ---------- - trex_cmd_options : str - Defines the exact command to run on the t-rex - Example: "-c 2 -m 0.500000 -d 100 -f cap2/sfr.yaml --nc -p -l 1000" + kwargs: dictionary + Dictionary of parameters for trex. For example: (c=1, nc=True, l_pkt_mode=3). + Notice that when sending command line parameters that has -, you need to replace it with _. + for example, to have on command line "--l-pkt-mode 3", you need to send l_pkt_mode=3 export_path : str - a full system path to which the results of the trex-run will be logged. + Full system path to which the results of the trex-run will be logged. """ if 'results_file_path' in kwargs: -- cgit From feb152b7d5eb531d439a4950d27cbc44031daf92 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 8 Dec 2015 07:43:24 -0500 Subject: text based TUI (does not use ncurses) --- .../client/trex_stateless_client.py | 8 +- .../trex_control_plane/console/old_console.py | 958 --------------------- .../trex_control_plane/console/trex_console.py | 6 +- .../trex_control_plane/console/trex_tui.py | 309 +++++++ 4 files changed, 319 insertions(+), 962 deletions(-) delete mode 100644 scripts/automation/trex_control_plane/console/old_console.py create mode 100644 scripts/automation/trex_control_plane/console/trex_tui.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 0c6d9855..0a583f46 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -321,6 +321,8 @@ class Port(object): self.streams[stream_id] = None + self.state = self.STATE_STREAMS if len(self.streams > 0) else self.STATE_IDLE + return self.ok() # remove all the streams @@ -335,6 +337,8 @@ class Port(object): self.streams = {} + self.state = self.STATE_IDLE + return self.ok() # get a specific stream @@ -1364,12 +1368,13 @@ class CTRexStatelessClient(object): # set to show all stats if no filter was given mask = trex_stats.ALL_STATS_OPTS - # get stats objects, as dictionary stats = self.cmd_stats(opts.ports, mask) + # print stats to screen for stat_type, stat_data in stats.iteritems(): text_tables.print_table_with_header(stat_data.text_table, stat_type) + return RC_OK() @@ -1481,6 +1486,7 @@ class CTRexStatelessClient(object): def _filter_namespace_args(namespace, ok_values): return {k: v for k, v in namespace.__dict__.items() if k in ok_values} + ################################# # ------ private classes ------ # class CCommLink(object): diff --git a/scripts/automation/trex_control_plane/console/old_console.py b/scripts/automation/trex_control_plane/console/old_console.py deleted file mode 100644 index 9d61a3a6..00000000 --- a/scripts/automation/trex_control_plane/console/old_console.py +++ /dev/null @@ -1,958 +0,0 @@ - -# main console object -class TRexConsole1(cmd.Cmd): - """Trex Console""" - - def __init__(self, stateless_client, verbose): - cmd.Cmd.__init__(self) - - self.stateless_client = stateless_client - - self.do_connect("") - - self.intro = "\n-=TRex Console v{ver}=-\n".format(ver=__version__) - self.intro += "\nType 'help' or '?' for supported actions\n" - - self.verbose = False - self._silent = True - - self.postcmd(False, "") - - self.user_streams = {} - self.streams_db = CStreamsDB() - - - # a cool hack - i stole this function and added space - def completenames(self, text, *ignored): - dotext = 'do_'+text - return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)] - - - # set verbose on / off - def do_verbose(self, line): - '''Shows or set verbose mode\n''' - if line == "": - print "\nverbose is " + ("on\n" if self.verbose else "off\n") - - elif line == "on": - self.verbose = True - self.stateless_client.set_verbose(True) - print green("\nverbose set to on\n") - - elif line == "off": - self.verbose = False - self.stateless_client.set_verbose(False) - print green("\nverbose set to off\n") - - else: - print magenta("\nplease specify 'on' or 'off'\n") - - # query the server for registered commands - def do_query_server(self, line): - '''query the RPC server for supported remote commands\n''' - - res_ok, msg = self.stateless_client.get_supported_cmds() - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print "\nRPC server supports the following commands:\n" - for func in msg: - if func: - print func - print '' - print format_text("[SUCCESS]\n", 'green', 'bold') - return - - def do_ping(self, line): - '''Pings the RPC server\n''' - - print "\n-> Pinging RPC server" - - res_ok, msg = self.stateless_client.ping() - if res_ok: - print format_text("[SUCCESS]\n", 'green', 'bold') - else: - print "\n*** " + msg + "\n" - return - - def do_force_acquire(self, line): - '''Acquires ports by force\n''' - - self.do_acquire(line, True) - - def complete_force_acquire(self, text, line, begidx, endidx): - return self.port_auto_complete(text, line, begidx, endidx, acquired=False) - - def extract_port_ids_from_line(self, line): - return {int(x) for x in line.split()} - - def extract_port_ids_from_list(self, port_list): - return {int(x) for x in port_list} - - def parse_ports_from_line (self, line): - port_list = set() - if line: - for port_id in line.split(' '): - if (not port_id.isdigit()) or (int(port_id) < 0) or (int(port_id) >= self.stateless_client.get_port_count()): - print "Please provide a list of ports separated by spaces between 0 and {0}".format(self.stateless_client.get_port_count() - 1) - return None - - port_list.add(int(port_id)) - - port_list = list(port_list) - - else: - port_list = [i for i in xrange(0, self.stateless_client.get_port_count())] - - return port_list - - - def do_acquire(self, line, force=False): - '''Acquire ports\n''' - - # make sure that the user wants to acquire all - args = line.split() - if len(args) < 1: - print magenta("Please provide a list of ports separated by spaces, or specify 'all' to acquire all available ports") - return - - if args[0] == "all": - ask = ConfirmMenu('Are you sure you want to acquire all ports ? ') - rc = ask.show() - if rc == False: - print yellow("[ABORTED]\n") - return - else: - port_list = self.stateless_client.get_port_ids() - else: - port_list = self.extract_port_ids_from_line(line) - - # rc, resp_list = self.stateless_client.take_ownership(port_list, force) - try: - res_ok, log = self.stateless_client.acquire(port_list, force) - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - except ValueError as e: - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - - - def port_auto_complete(self, text, line, begidx, endidx, acquired=True, active=False): - if acquired: - if not active: - ret_list = [x - for x in map(str, self.stateless_client.get_acquired_ports()) - if x.startswith(text)] - else: - ret_list = [x - for x in map(str, self.stateless_client.get_active_ports()) - if x.startswith(text)] - else: - ret_list = [x - for x in map(str, self.stateless_client.get_port_ids()) - if x.startswith(text)] - ret_list.append("all") - return ret_list - - - def complete_acquire(self, text, line, begidx, endidx): - return self.port_auto_complete(text, line, begidx, endidx, acquired=False) - - def do_release (self, line): - '''Release ports\n''' - - # if line: - # port_list = self.parse_ports_from_line(line) - # else: - # port_list = self.stateless_client.get_owned_ports() - args = line.split() - if len(args) < 1: - print "Please provide a list of ports separated by spaces, or specify 'all' to acquire all available ports" - if args[0] == "all": - ask = ConfirmMenu('Are you sure you want to release all acquired ports? ') - rc = ask.show() - if rc == False: - print yellow("[ABORTED]\n") - return - else: - port_list = self.stateless_client.get_acquired_ports() - else: - port_list = self.extract_port_ids_from_line(line) - - try: - res_ok, log = self.stateless_client.release(port_list) - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - except ValueError as e: - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - return - - def complete_release(self, text, line, begidx, endidx): - return self.port_auto_complete(text, line, begidx, endidx) - - def do_connect (self, line): - '''Connects to the server\n''' - - if line == "": - res_ok, msg = self.stateless_client.connect() - else: - sp = line.split() - if (len(sp) != 2): - print "\n[usage] connect [server] [port] or without parameters\n" - return - - res_ok, msg = self.stateless_client.connect(sp[0], sp[1]) - - if res_ok: - print format_text("[SUCCESS]\n", 'green', 'bold') - else: - print "\n*** " + msg + "\n" - print format_text("[FAILED]\n", 'red', 'bold') - return - - self.supported_rpc = self.stateless_client.get_supported_cmds().data - - # def do_rpc (self, line): - # '''Launches a RPC on the server\n''' - # - # if line == "": - # print "\nUsage: [method name] [param dict as string]\n" - # print "Example: rpc test_add {'x': 12, 'y': 17}\n" - # return - # - # sp = line.split(' ', 1) - # method = sp[0] - # - # params = None - # bad_parse = False - # if len(sp) > 1: - # - # try: - # params = ast.literal_eval(sp[1]) - # if not isinstance(params, dict): - # bad_parse = True - # - # except ValueError as e1: - # bad_parse = True - # except SyntaxError as e2: - # bad_parse = True - # - # if bad_parse: - # print "\nValue should be a valid dict: '{0}'".format(sp[1]) - # print "\nUsage: [method name] [param dict as string]\n" - # print "Example: rpc test_add {'x': 12, 'y': 17}\n" - # return - # - # res_ok, msg = self.stateless_client.transmit(method, params) - # if res_ok: - # print "\nServer Response:\n\n" + pretty_json(json.dumps(msg)) + "\n" - # else: - # print "\n*** " + msg + "\n" - # #print "Please try 'reconnect' to reconnect to server" - # - # - # def complete_rpc (self, text, line, begidx, endidx): - # return [x - # for x in self.supported_rpc - # if x.startswith(text)] - - def do_status (self, line): - '''Shows a graphical console\n''' - - if not self.stateless_client.is_connected(): - print "Not connected to server\n" - return - - self.do_verbose('off') - trex_status.show_trex_status(self.stateless_client) - - def do_quit(self, line): - '''Exit the client\n''' - return True - - def do_disconnect (self, line): - '''Disconnect from the server\n''' - if not self.stateless_client.is_connected(): - print "Not connected to server\n" - return - - res_ok, msg = self.stateless_client.disconnect() - if res_ok: - print format_text("[SUCCESS]\n", 'green', 'bold') - else: - print msg + "\n" - - def do_whoami (self, line): - '''Prints console user name\n''' - print "\n" + self.stateless_client.user + "\n" - - def postcmd(self, stop, line): - if self.stateless_client.is_connected(): - self.prompt = "TRex > " - else: - self.supported_rpc = None - self.prompt = "TRex (offline) > " - - return stop - - def default(self, line): - print "'{0}' is an unrecognized command. type 'help' or '?' for a list\n".format(line) - - # def do_help (self, line): - # '''Shows This Help Screen\n''' - # if line: - # try: - # func = getattr(self, 'help_' + line) - # except AttributeError: - # try: - # doc = getattr(self, 'do_' + line).__doc__ - # if doc: - # self.stdout.write("%s\n"%str(doc)) - # return - # except AttributeError: - # pass - # self.stdout.write("%s\n"%str(self.nohelp % (line,))) - # return - # func() - # return - # - # print "\nSupported Console Commands:" - # print "----------------------------\n" - # - # cmds = [x[3:] for x in self.get_names() if x.startswith("do_")] - # for cmd in cmds: - # if cmd == "EOF": - # continue - # - # try: - # doc = getattr(self, 'do_' + cmd).__doc__ - # if doc: - # help = str(doc) - # else: - # help = "*** Undocumented Function ***\n" - # except AttributeError: - # help = "*** Undocumented Function ***\n" - # - # print "{:<30} {:<30}".format(cmd + " - ", help) - - def do_stream_db_add(self, line): - '''Loads a YAML stream list serialization into user console \n''' - args = line.split() - if len(args) >= 2: - name = args[0] - yaml_path = args[1] - try: - multiplier = args[2] - except IndexError: - multiplier = 1 - stream_list = CStreamList() - loaded_obj = stream_list.load_yaml(yaml_path, multiplier) - # print self.stateless_client.pretty_json(json.dumps(loaded_obj)) - try: - compiled_streams = stream_list.compile_streams() - res_ok = self.streams_db.load_streams(name, LoadedStreamList(loaded_obj, - [StreamPack(v.stream_id, v.stream.dump()) - for k, v in compiled_streams.items()])) - if res_ok: - print green("Stream pack '{0}' loaded and added successfully\n".format(name)) - else: - print magenta("Picked name already exist. Please pick another name.\n") - except Exception as e: - print "adding new stream failed due to the following error:\n", str(e) - print format_text("[FAILED]\n", 'red', 'bold') - - return - else: - print magenta("please provide load name and YAML path, separated by space.\n" - "Optionally, you may provide a third argument to specify multiplier.\n") - - @staticmethod - def tree_autocomplete(text): - dir = os.path.dirname(text) - if dir: - path = dir - else: - path = "." - start_string = os.path.basename(text) - return [x - for x in os.listdir(path) - if x.startswith(start_string)] - - - def complete_stream_db_add(self, text, line, begidx, endidx): - arg_num = len(line.split()) - 1 - if arg_num == 2: - return TRexConsole.tree_autocomplete(line.split()[-1]) - else: - return [text] - - def do_stream_db_show(self, line): - '''Shows the loaded stream list named [name] \n''' - args = line.split() - if args: - list_name = args[0] - try: - stream = self.streams_db.get_stream_pack(list_name)#user_streams[list_name] - if len(args) >= 2 and args[1] == "full": - print pretty_json(json.dumps(stream.compiled)) - else: - print pretty_json(json.dumps(stream.loaded)) - except KeyError as e: - print "Unknown stream list name provided" - else: - print "Available stream packs:\n{0}".format(', '.join(sorted(self.streams_db.get_loaded_streams_names()))) - - def complete_stream_db_show(self, text, line, begidx, endidx): - return [x - for x in self.streams_db.get_loaded_streams_names() - if x.startswith(text)] - - def do_stream_db_remove(self, line): - '''Removes a single loaded stream packs from loaded stream pack repository\n''' - args = line.split() - if args: - removed_streams = self.streams_db.remove_stream_packs(*args) - if removed_streams: - print green("The following stream packs were removed:") - print bold(", ".join(sorted(removed_streams))) - print format_text("[SUCCESS]\n", 'green', 'bold') - else: - print red("No streams were removed. Make sure to provide valid stream pack names.") - else: - print magenta("Please provide stream pack name(s), separated with spaces.") - - def do_stream_db_clear(self, line): - '''Clears all loaded stream packs from loaded stream pack repository\n''' - self.streams_db.clear() - print format_text("[SUCCESS]\n", 'green', 'bold') - - - def complete_stream_db_remove(self, text, line, begidx, endidx): - return [x - for x in self.streams_db.get_loaded_streams_names() - if x.startswith(text)] - - - def do_attach(self, line): - '''Assign loaded stream pack into specified ports on TRex\n''' - args = line.split() - if len(args) >= 2: - stream_pack_name = args[0] - stream_list = self.streams_db.get_stream_pack(stream_pack_name) #user_streams[args[0]] - if not stream_list: - print "Provided stream list name '{0}' doesn't exists.".format(stream_pack_name) - print format_text("[FAILED]\n", 'red', 'bold') - return - if args[1] == "all": - ask = ConfirmMenu('Are you sure you want to release all acquired ports? ') - rc = ask.show() - if rc == False: - print yellow("[ABORTED]\n") - return - else: - port_list = self.stateless_client.get_acquired_ports() - else: - port_list = self.extract_port_ids_from_line(' '.join(args[1:])) - owned = set(self.stateless_client.get_acquired_ports()) - try: - if set(port_list).issubset(owned): - res_ok, log = self.stateless_client.add_stream_pack(stream_list.compiled, port_id=port_list) - # res_ok, msg = self.stateless_client.add_stream(port_list, stream_list.compiled) - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - return - else: - print "Not all desired ports are acquired.\n" \ - "Acquired ports are: {acq}\n" \ - "Requested ports: {req}\n" \ - "Missing ports: {miss}".format(acq=list(owned), - req=port_list, - miss=list(set(port_list).difference(owned))) - print format_text("[FAILED]\n", 'red', 'bold') - except ValueError as e: - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - else: - print magenta("Please provide list name and ports to attach to, " - "or specify 'all' to attach all owned ports.\n") - - def complete_attach(self, text, line, begidx, endidx): - arg_num = len(line.split()) - 1 - if arg_num == 1: - # return optional streams packs - if line.endswith(" "): - return self.port_auto_complete(text, line, begidx, endidx) - return [x - for x in self.streams_db.get_loaded_streams_names() - if x.startswith(text)] - elif arg_num >= 2: - # return optional ports to attach to - return self.port_auto_complete(text, line, begidx, endidx) - else: - return [text] - - def prompt_response(self, response_obj): - resp_list = response_obj if isinstance(response_obj, list) else [response_obj] - def format_return_status(return_status): - if return_status: - return green("OK") - else: - return red("FAIL") - - for response in resp_list: - response_str = "{id:^3} - {msg} ({stat})".format(id=response.id, - msg=response.msg, - stat=format_return_status(response.success)) - print response_str - return - - def do_remove_all_streams(self, line): - '''Acquire ports\n''' - - # make sure that the user wants to acquire all - args = line.split() - if len(args) < 1: - print magenta("Please provide a list of ports separated by spaces, " - "or specify 'all' to remove from all acquired ports") - return - if args[0] == "all": - ask = ConfirmMenu('Are you sure you want to remove all stream packs from all acquired ports? ') - rc = ask.show() - if rc == False: - print yellow("[ABORTED]\n") - return - else: - port_list = self.stateless_client.get_acquired_ports() - else: - port_list = self.extract_port_ids_from_line(line) - - # rc, resp_list = self.stateless_client.take_ownership(port_list, force) - try: - res_ok, log = self.stateless_client.remove_all_streams(port_list) - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - except ValueError as e: - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - - def complete_remove_all_streams(self, text, line, begidx, endidx): - return self.port_auto_complete(text, line, begidx, endidx) - - def do_start(self, line): - '''Start selected traffic in specified ports on TRex\n''' - # make sure that the user wants to acquire all - parser = parsing_opts.gen_parser("start", self.do_start.__doc__, - parsing_opts.PORT_LIST_WITH_ALL, - parsing_opts.FORCE, - parsing_opts.STREAM_FROM_PATH_OR_FILE, - parsing_opts.DURATION, - parsing_opts.MULTIPLIER) - opts = parser.parse_args(line.split()) - if opts is None: - # avoid further processing in this command - return - # print opts - port_list = self.extract_port_list(opts) - # print port_list - if opts.force: - # stop all active ports, if any - res_ok = self.stop_traffic(set(self.stateless_client.get_active_ports()).intersection(port_list)) - if not res_ok: - print yellow("[ABORTED]\n") - return - # remove all traffic from ports - res_ok = self.remove_all_streams(port_list) - if not res_ok: - print yellow("[ABORTED]\n") - return - # decide which traffic to use - stream_pack_name = None - if opts.db: - # use pre-loaded traffic - print format_text('{:<30}'.format("Load stream pack (from DB):"), 'bold'), - if opts.db not in self.streams_db.get_loaded_streams_names(): - print format_text("[FAILED]\n", 'red', 'bold') - print yellow("[ABORTED]\n") - return - else: - stream_pack_name = opts.db - else: - # try loading a YAML file - print format_text('{:<30}'.format("Load stream pack (from file):"), 'bold'), - stream_list = CStreamList() - loaded_obj = stream_list.load_yaml(opts.file[0]) - # print self.stateless_client.pretty_json(json.dumps(loaded_obj)) - try: - compiled_streams = stream_list.compile_streams() - res_ok = self.streams_db.load_streams(opts.file[1], - LoadedStreamList(loaded_obj, - [StreamPack(v.stream_id, v.stream.dump()) - for k, v in compiled_streams.items()])) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - print yellow("[ABORTED]\n") - return - print format_text("[SUCCESS]\n", 'green', 'bold') - stream_pack_name = opts.file[1] - except Exception as e: - print format_text("[FAILED]\n", 'red', 'bold') - print yellow("[ABORTED]\n") - res_ok = self.attach_to_port(stream_pack_name, port_list) - if not res_ok: - print yellow("[ABORTED]\n") - return - # finally, start the traffic - res_ok = self.start_traffic(opts.mult, port_list) - if not res_ok: - print yellow("[ABORTED]\n") - return - return - - def help_start(self): - self.do_start("-h") - - def do_stop(self, line): - '''Stop active traffic in specified ports on TRex\n''' - parser = parsing_opts.gen_parser("stop", self.do_stop.__doc__, - parsing_opts.PORT_LIST_WITH_ALL) - opts = parser.parse_args(line.split()) - if opts is None: - # avoid further processing in this command - return - port_list = self.extract_port_list(opts) - res_ok = self.stop_traffic(port_list) - return - - def do_pause(self, line): - '''Pause active traffic in specified ports on TRex\n''' - parser = parsing_opts.gen_parser("stop", self.do_stop.__doc__, - parsing_opts.PORT_LIST_WITH_ALL) - opts = parser.parse_args(line.split()) - if opts is None: - # avoid further processing in this command - return - port_list = self.extract_port_list(opts) - res_ok = self.stop_traffic(port_list) - return - - - def help_stop(self): - self.do_stop("-h") - - - def do_debug(self, line): - '''Enter DEBUG mode of the console to invoke smaller building blocks with server''' - i = DebugTRexConsole(self) - i.prompt = self.prompt[:-3] + ':' + blue('debug') + ' > ' - i.cmdloop() - - # aliasing - do_exit = do_EOF = do_q = do_quit - - # ----- utility methods ----- # - - def start_traffic(self, multiplier, port_list):#, silent=True): - print format_text('{:<30}'.format("Start traffic:"), 'bold'), - try: - res_ok, log = self.stateless_client.start_traffic(multiplier, port_id=port_list) - if not self._silent: - print '' - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return False - print format_text("[SUCCESS]\n", 'green', 'bold') - return True - except ValueError as e: - print '' - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - return False - - def attach_to_port(self, stream_pack_name, port_list): - print format_text('{:<30}'.format("Attaching traffic to ports:"), 'bold'), - stream_list = self.streams_db.get_stream_pack(stream_pack_name) #user_streams[args[0]] - if not stream_list: - print "Provided stream list name '{0}' doesn't exists.".format(stream_pack_name) - print format_text("[FAILED]\n", 'red', 'bold') - return - try: - res_ok, log = self.stateless_client.add_stream_pack(stream_list.compiled, port_id=port_list) - if not self._silent: - print '' - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return False - print format_text("[SUCCESS]\n", 'green', 'bold') - return True - except ValueError as e: - print '' - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - return False - - def stop_traffic(self, port_list): - print format_text('{:<30}'.format("Stop traffic:"), 'bold'), - try: - res_ok, log = self.stateless_client.stop_traffic(port_id=port_list) - if not self._silent: - print '' - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - return True - except ValueError as e: - print '' - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - - def remove_all_streams(self, port_list): - '''Remove all streams from given port_list''' - print format_text('{:<30}'.format("Remove all streams:"), 'bold'), - try: - res_ok, log = self.stateless_client.remove_all_streams(port_id=port_list) - if not self._silent: - print '' - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - return True - except ValueError as e: - print '' - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - - - - - - def extract_port_list(self, opts): - if opts.all_ports or "all" in opts.ports: - # handling all ports - port_list = self.stateless_client.get_acquired_ports() - else: - port_list = self.extract_port_ids_from_list(opts.ports) - return port_list - - def decode_multiplier(self, opts_mult): - pass - - -class DebugTRexConsole(cmd.Cmd): - - def __init__(self, trex_main_console): - cmd.Cmd.__init__(self) - self.trex_console = trex_main_console - self.stateless_client = self.trex_console.stateless_client - self.streams_db = self.trex_console.streams_db - self.register_main_console_methods() - self.do_silent("on") - pass - - # ----- super methods overriding ----- # - def completenames(self, text, *ignored): - dotext = 'do_'+text - return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)] - - def get_names(self): - result = cmd.Cmd.get_names(self) - result += self.trex_console.get_names() - return list(set(result)) - - def register_main_console_methods(self): - main_names = set(self.trex_console.get_names()).difference(set(dir(self.__class__))) - for name in main_names: - for prefix in 'do_', 'help_', 'complete_': - if name.startswith(prefix): - self.__dict__[name] = getattr(self.trex_console, name) - - # if (name[:3] == 'do_') or (name[:5] == 'help_') or (name[:9] == 'complete_'): - # chosen.append(name) - # self.__dict__[name] = getattr(self.trex_console, name) - # # setattr(self, name, classmethod(getattr(self.trex_console, name))) - - # print chosen - # self.get_names() - - # return result - - - # ----- DEBUGGING methods ----- # - # set silent on / off - def do_silent(self, line): - '''Shows or set silent mode\n''' - if line == "": - print "\nsilent mode is " + ("on\n" if self.trex_console._silent else "off\n") - - elif line == "on": - self.verbose = True - self.stateless_client.set_verbose(True) - print green("\nsilent set to on\n") - - elif line == "off": - self.verbose = False - self.stateless_client.set_verbose(False) - print green("\nsilent set to off\n") - - else: - print magenta("\nplease specify 'on' or 'off'\n") - - def do_quit(self, line): - '''Exit the debug client back to main console\n''' - self.do_silent("off") - return True - - def do_start_traffic(self, line): - '''Start pre-submitted traffic in specified ports on TRex\n''' - # make sure that the user wants to acquire all - parser = parsing_opts.gen_parser("start_traffic", self.do_start_traffic.__doc__, - parsing_opts.PORT_LIST_WITH_ALL, parsing_opts.MULTIPLIER) - opts = parser.parse_args(line.split()) - # print opts - # return - if opts is None: - # avoid further processing in this command - return - try: - port_list = self.trex_console.extract_port_list(opts) - return self.trex_console.start_traffic(opts.mult, port_list) - except Exception as e: - print e - return - - def do_stop_traffic(self, line): - '''Stop active traffic in specified ports on TRex\n''' - parser = parsing_opts.gen_parser("stop_traffic", self.do_stop_traffic.__doc__, - parsing_opts.PORT_LIST_WITH_ALL) - opts = parser.parse_args(line.split()) - # print opts - # return - if opts is None: - # avoid further processing in this command - return - try: - port_list = self.trex_console.extract_port_list(opts) - return self.trex_console.stop_traffic(port_list) - except Exception as e: - print e - return - - - def complete_stop_traffic(self, text, line, begidx, endidx): - return self.port_auto_complete(text, line, begidx, endidx, active=True) - - # return - # # return - # # if not opts.port_list: - # # print magenta("Please provide a list of ports separated by spaces, " - # # "or specify 'all' to start traffic on all acquired ports") - # # return - # - - - return - args = line.split() - if len(args) < 1: - print magenta("Please provide a list of ports separated by spaces, " - "or specify 'all' to start traffic on all acquired ports") - return - if args[0] == "all": - ask = ConfirmMenu('Are you sure you want to start traffic at all acquired ports? ') - rc = ask.show() - if rc == False: - print yellow("[ABORTED]\n") - return - else: - port_list = self.stateless_client.get_acquired_ports() - else: - port_list = self.extract_port_ids_from_line(line) - - try: - res_ok, log = self.stateless_client.start_traffic(1.0, port_id=port_list) - self.prompt_response(log) - if not res_ok: - print format_text("[FAILED]\n", 'red', 'bold') - return - print format_text("[SUCCESS]\n", 'green', 'bold') - except ValueError as e: - print magenta(str(e)) - print format_text("[FAILED]\n", 'red', 'bold') - - def complete_start_traffic(self, text, line, begidx, endidx): - # return self.port_auto_complete(text, line, begidx, endidx) - return [text] - - def help_start_traffic(self): - self.do_start_traffic("-h") - - def help_stop_traffic(self): - self.do_stop_traffic("-h") - - # def do_help(self): - - def do_rpc (self, line): - '''Launches a RPC on the server\n''' - - if line == "": - print "\nUsage: [method name] [param dict as string]\n" - print "Example: rpc test_add {'x': 12, 'y': 17}\n" - return - - sp = line.split(' ', 1) - method = sp[0] - - params = None - bad_parse = False - if len(sp) > 1: - - try: - params = ast.literal_eval(sp[1]) - if not isinstance(params, dict): - bad_parse = True - - except ValueError as e1: - bad_parse = True - except SyntaxError as e2: - bad_parse = True - - if bad_parse: - print "\nValue should be a valid dict: '{0}'".format(sp[1]) - print "\nUsage: [method name] [param dict as string]\n" - print "Example: rpc test_add {'x': 12, 'y': 17}\n" - return - - res_ok, msg = self.stateless_client.transmit(method, params) - if res_ok: - print "\nServer Response:\n\n" + pretty_json(json.dumps(msg)) + "\n" - else: - print "\n*** " + msg + "\n" - #print "Please try 'reconnect' to reconnect to server" - - - def complete_rpc (self, text, line, begidx, endidx): - return [x - for x in self.trex_console.supported_rpc - if x.startswith(text)] - - # aliasing - do_exit = do_EOF = do_q = do_quit - -# diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index e187c8c2..8d52b2a4 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -34,8 +34,7 @@ from client.trex_stateless_client import CTRexStatelessClient from common.text_opts import * from client_utils.general_utils import user_input, get_current_user from client_utils import parsing_opts -import trex_status -import parsing_opts +import trex_tui from functools import wraps @@ -119,6 +118,7 @@ class TRexConsole(TRexGeneralCmd): self.stateless_client = stateless_client TRexGeneralCmd.__init__(self) + self.tui = trex_tui.TrexTUI(stateless_client) self.verbose = verbose self.acquire_all_ports = acquire_all_ports @@ -425,7 +425,7 @@ class TRexConsole(TRexGeneralCmd): '''Shows a graphical console\n''' self.do_verbose('off') - trex_status.show_trex_status(self.stateless_client) + self.tui.show() # quit function def do_quit(self, line): diff --git a/scripts/automation/trex_control_plane/console/trex_tui.py b/scripts/automation/trex_control_plane/console/trex_tui.py new file mode 100644 index 00000000..e835ed24 --- /dev/null +++ b/scripts/automation/trex_control_plane/console/trex_tui.py @@ -0,0 +1,309 @@ +import termios +import sys +import os +import time +from common.text_opts import * +from common import trex_stats +from client_utils import text_tables +from collections import OrderedDict +import datetime + +# base type of a panel +class TrexTUIPanel(object): + def __init__ (self, mng, name): + + self.mng = mng + self.name = name + self.stateless_client = mng.stateless_client + + + def show (self): + raise Exception("must implement this") + + def get_key_actions (self): + raise Exception("must implement this") + + def get_name (self): + return self.name + + +# dashboard panel +class TrexTUIDashBoard(TrexTUIPanel): + def __init__ (self, mng): + super(TrexTUIDashBoard, self).__init__(mng, "dashboard") + + self.key_actions = OrderedDict() + self.key_actions['p'] = {'action': self.action_pause, 'legend': 'pause', 'show': True} + self.key_actions['r'] = {'action': self.action_resume, 'legend': 'resume', 'show': True} + self.key_actions['+'] = {'action': self.action_raise, 'legend': 'up 5%', 'show': True} + self.key_actions['-'] = {'action': self.action_lower, 'legend': 'low 5%', 'show': True} + + def show (self): + ports = self.stateless_client.get_acquired_ports() + stats = self.stateless_client.cmd_stats(ports, trex_stats.ALL_STATS_OPTS) + # print stats to screen + for stat_type, stat_data in stats.iteritems(): + text_tables.print_table_with_header(stat_data.text_table, stat_type) + + def get_key_actions (self): + return self.key_actions + + # actions + def action_pause (self): + rc = self.stateless_client.pause_traffic(self.mng.acquired_ports) + if rc.good(): + return "paused traffic on all ports" + else: + return "" + + def action_resume (self): + rc = self.stateless_client.resume_traffic(self.mng.acquired_ports) + if rc.good(): + return "resumed traffic on all ports" + else: + return "" + + def action_raise (self): + mul = {'type': 'percentage', 'value': 5, 'op': 'add'} + rc = self.stateless_client.update_traffic(mul, self.mng.acquired_ports) + + if rc.good(): + return "raised B/W by 5% on all ports" + else: + return "" + + def action_lower (self): + mul = {'type': 'percentage', 'value': 5, 'op': 'sub'} + rc = self.stateless_client.update_traffic(mul, self.mng.acquired_ports) + + if rc.good(): + return "lowered B/W by 5% on all ports" + else: + return "" + + +# port panel +class TrexTUIPort(TrexTUIPanel): + def __init__ (self, mng, port_id): + super(TrexTUIPort, self).__init__(mng, "port {0}".format(port_id)) + self.port_id = port_id + + self.key_actions = OrderedDict() + self.key_actions['p'] = {'action': self.action_pause, 'legend': 'pause', 'show': True} + self.key_actions['r'] = {'action': self.action_resume, 'legend': 'resume', 'show': True} + self.key_actions['+'] = {'action': self.action_raise, 'legend': 'up 5%', 'show': True} + self.key_actions['-'] = {'action': self.action_lower, 'legend': 'low 5%', 'show': True} + + def show (self): + stats = self.stateless_client.cmd_stats([self.port_id], trex_stats.ALL_STATS_OPTS) + # print stats to screen + for stat_type, stat_data in stats.iteritems(): + text_tables.print_table_with_header(stat_data.text_table, stat_type) + + def get_key_actions (self): + return self.key_actions + + # actions + def action_pause (self): + rc = self.stateless_client.pause_traffic([self.port_id]) + if rc.good(): + return "paused traffic on port {0}".format(self.port_id) + else: + return "" + + def action_resume (self): + rc = self.stateless_client.resume_traffic([self.port_id]) + if rc.good(): + return "resumed traffic on port {0}".format(self.port_id) + else: + return "" + + def action_raise (self): + mul = {'type': 'percentage', 'value': 5, 'op': 'add'} + rc = self.stateless_client.update_traffic(mul, [self.port_id]) + + if rc.good(): + return "raised B/W by 5% on port {0}".format(self.port_id) + else: + return "" + + def action_lower (self): + mul = {'type': 'percentage', 'value': 5, 'op': 'sub'} + rc = self.stateless_client.update_traffic(mul, [self.port_id]) + + if rc.good(): + return "lowered B/W by 5% on port {0}".format(self.port_id) + else: + return "" + +# log +class TrexTUILog(): + def __init__ (self): + self.log = [] + + def add_event (self, msg): + self.log.append("[{0}] {1}".format(str(datetime.datetime.now().time()), msg)) + + def show (self, max_lines = 4): + cut = len(self.log) - max_lines + if cut < 0: + cut = 0 + + print format_text("\nLog:", 'bold', 'underline') + + for msg in self.log[cut:]: + print msg + + +# Panels manager (contains server panels) +class TrexTUIPanelManager(): + def __init__ (self, tui): + self.tui = tui + self.stateless_client = tui.stateless_client + self.acquired_ports = self.stateless_client.get_acquired_ports() + + self.panels = {} + self.panels['dashboard'] = TrexTUIDashBoard(self) + + self.key_actions = OrderedDict() + self.key_actions['q'] = {'action': self.action_quit, 'legend': 'quit', 'show': True} + self.key_actions['g'] = {'action': self.action_show_dash, 'legend': 'dashboard', 'show': True} + + for port_id in self.acquired_ports: + self.key_actions[str(port_id)] = {'action': self.action_show_port(port_id), 'legend': 'port {0}'.format(port_id), 'show': False} + self.panels['port {0}'.format(port_id)] = TrexTUIPort(self, port_id) + + # start with dashboard + self.main_panel = self.panels['dashboard'] + + self.generate_legend() + + self.log = TrexTUILog() + + + def generate_legend (self): + self.legend = "{:<12}".format("browse:") + + for k, v in self.key_actions.iteritems(): + if v['show']: + x = "'{0}' - {1}, ".format(k, v['legend']) + self.legend += "{:}".format(x) + + self.legend += "'0-{0}' - port display".format(len(self.acquired_ports) - 1) + + panel_actions = self.main_panel.get_key_actions() + if not panel_actions: + return + + self.legend += "\n{:<12}".format(self.main_panel.get_name() + ":") + for k, v in self.main_panel.get_key_actions().iteritems(): + if v['show']: + x = "'{0}' - {1}, ".format(k, v['legend']) + self.legend += "{:}".format(x) + + + def print_legend (self): + print format_text(self.legend, 'bold') + + + def show (self): + self.main_panel.show() + self.print_legend() + self.log.show() + + + def handle_key (self, ch): + # check for the manager registered actions + if ch in self.key_actions: + msg = self.key_actions[ch]['action']() + + # check for main panel actions + elif ch in self.main_panel.get_key_actions(): + msg = self.main_panel.get_key_actions()[ch]['action']() + + else: + msg = "" + + self.generate_legend() + + if msg == None: + return False + else: + if msg: + self.log.add_event(msg) + return True + + + # actions + + def action_quit (self): + return None + + def action_show_dash (self): + self.main_panel = self.panels['dashboard'] + return "" + + def action_show_port (self, port_id): + def action_show_port_x (): + self.main_panel = self.panels['port {0}'.format(port_id)] + return "" + + return action_show_port_x + + + +# shows a textual top style window +class TrexTUI(): + def __init__ (self, stateless_client): + self.stateless_client = stateless_client + + self.pm = TrexTUIPanelManager(self) + + + + def handle_key_input (self): + # try to read a single key + ch = os.read(sys.stdin.fileno(), 1) + if ch != None and len(ch) > 0: + return self.pm.handle_key(ch) + + else: + return True + + + def clear_screen (self): + os.system('clear') + + + + def show (self): + # init termios + old_settings = termios.tcgetattr(sys.stdin) + new_settings = termios.tcgetattr(sys.stdin) + new_settings[3] = new_settings[3] & ~(termios.ECHO | termios.ICANON) # lflags + new_settings[6][termios.VMIN] = 0 # cc + new_settings[6][termios.VTIME] = 0 # cc + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new_settings) + + try: + while True: + self.clear_screen() + + cont = self.handle_key_input() + self.pm.show() + + if not cont: + break + + time.sleep(0.1) + + finally: + # restore + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) + + print "" + + # key actions + def action_quit (self): + return False + -- cgit From 8713749a155b8d1a7e140d073f0092e3b62cacac Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 8 Dec 2015 08:42:08 -0500 Subject: a minor fix to the TUI (pause/resume all ports) --- .../client/trex_stateless_client.py | 4 +-- .../trex_control_plane/console/trex_tui.py | 40 +++++++++++++++++----- 2 files changed, 34 insertions(+), 10 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 0a583f46..f23e801c 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -75,7 +75,7 @@ class RC(): print format_text("[SUCCESS]\n", 'green', 'bold') -def RC_OK(data = None): +def RC_OK(data = ""): return RC(True, data) def RC_ERR (err): return RC(False, err) @@ -189,7 +189,7 @@ class Port(object): def err(self, msg): return RC_ERR("port {0} : {1}".format(self.port_id, msg)) - def ok(self, data = None): + def ok(self, data = "ACK"): return RC_OK(data) def get_speed_bps (self): diff --git a/scripts/automation/trex_control_plane/console/trex_tui.py b/scripts/automation/trex_control_plane/console/trex_tui.py index e835ed24..3b64a02f 100644 --- a/scripts/automation/trex_control_plane/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/console/trex_tui.py @@ -51,24 +51,43 @@ class TrexTUIDashBoard(TrexTUIPanel): # actions def action_pause (self): rc = self.stateless_client.pause_traffic(self.mng.acquired_ports) - if rc.good(): - return "paused traffic on all ports" + + ports_succeeded = [] + for rc_single, port_id in zip(rc.rc_list, self.mng.acquired_ports): + if rc_single.rc: + ports_succeeded.append(port_id) + + if len(ports_succeeded) > 0: + return "paused traffic on port(s): {0}".format(ports_succeeded) else: return "" + def action_resume (self): rc = self.stateless_client.resume_traffic(self.mng.acquired_ports) - if rc.good(): - return "resumed traffic on all ports" + + ports_succeeded = [] + for rc_single, port_id in zip(rc.rc_list, self.mng.acquired_ports): + if rc_single.rc: + ports_succeeded.append(port_id) + + if len(ports_succeeded) > 0: + return "resumed traffic on port(s): {0}".format(ports_succeeded) else: return "" + def action_raise (self): mul = {'type': 'percentage', 'value': 5, 'op': 'add'} rc = self.stateless_client.update_traffic(mul, self.mng.acquired_ports) - if rc.good(): - return "raised B/W by 5% on all ports" + ports_succeeded = [] + for rc_single, port_id in zip(rc.rc_list, self.mng.acquired_ports): + if rc_single.rc: + ports_succeeded.append(port_id) + + if len(ports_succeeded) > 0: + return "raised B/W by %5 on port(s): {0}".format(ports_succeeded) else: return "" @@ -76,8 +95,13 @@ class TrexTUIDashBoard(TrexTUIPanel): mul = {'type': 'percentage', 'value': 5, 'op': 'sub'} rc = self.stateless_client.update_traffic(mul, self.mng.acquired_ports) - if rc.good(): - return "lowered B/W by 5% on all ports" + ports_succeeded = [] + for rc_single, port_id in zip(rc.rc_list, self.mng.acquired_ports): + if rc_single.rc: + ports_succeeded.append(port_id) + + if len(ports_succeeded) > 0: + return "lowered B/W by %5 on port(s): {0}".format(ports_succeeded) else: return "" -- cgit From 8ed7433b1509c21e8840e8152c55e69b1a564dd4 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 8 Dec 2015 09:56:15 -0500 Subject: dynamic commands on TUI --- .../client/trex_stateless_client.py | 23 ++++++++ .../trex_control_plane/console/trex_tui.py | 66 +++++++++++++++++----- 2 files changed, 75 insertions(+), 14 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index f23e801c..72dc5e1b 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -228,6 +228,13 @@ class Port(object): def is_active(self): return(self.state == self.STATE_TX ) or (self.state == self.STATE_PAUSE) + def is_transmitting (self): + return (self.state == self.STATE_TX) + + def is_paused (self): + return (self.state == self.STATE_PAUSE) + + def sync(self, sync_data): self.handler = sync_data['handler'] port_state = sync_data['state'].upper() @@ -558,6 +565,12 @@ class CTRexStatelessClient(object): self.connected = False + + + # returns the port object + def get_port (self, port_id): + return self.ports.get(port_id, None) + ################# events handler ###################### def add_event_log (self, msg, ev_type, show = False): @@ -826,6 +839,16 @@ class CTRexStatelessClient(object): for port_id, port_obj in self.ports.iteritems() if port_obj.is_active()] + def get_paused_ports (self): + return [port_id + for port_id, port_obj in self.ports.iteritems() + if port_obj.is_paused()] + + def get_transmitting_ports (self): + return [port_id + for port_id, port_obj in self.ports.iteritems() + if port_obj.is_transmitting()] + def set_verbose(self, mode): self.comm_link.set_verbose(mode) self.verbose = mode diff --git a/scripts/automation/trex_control_plane/console/trex_tui.py b/scripts/automation/trex_control_plane/console/trex_tui.py index 3b64a02f..ea271985 100644 --- a/scripts/automation/trex_control_plane/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/console/trex_tui.py @@ -38,17 +38,32 @@ class TrexTUIDashBoard(TrexTUIPanel): self.key_actions['+'] = {'action': self.action_raise, 'legend': 'up 5%', 'show': True} self.key_actions['-'] = {'action': self.action_lower, 'legend': 'low 5%', 'show': True} + self.ports = self.stateless_client.get_acquired_ports() + + def show (self): - ports = self.stateless_client.get_acquired_ports() - stats = self.stateless_client.cmd_stats(ports, trex_stats.ALL_STATS_OPTS) + stats = self.stateless_client.cmd_stats(self.ports, trex_stats.ALL_STATS_OPTS) # print stats to screen for stat_type, stat_data in stats.iteritems(): text_tables.print_table_with_header(stat_data.text_table, stat_type) + def get_key_actions (self): - return self.key_actions + allowed = {} - # actions + if len(self.stateless_client.get_transmitting_ports()) > 0: + allowed['p'] = self.key_actions['p'] + allowed['+'] = self.key_actions['+'] + allowed['-'] = self.key_actions['-'] + + + if len(self.stateless_client.get_paused_ports()) > 0: + allowed['r'] = self.key_actions['r'] + + return allowed + + + ######### actions def action_pause (self): rc = self.stateless_client.pause_traffic(self.mng.acquired_ports) @@ -110,7 +125,9 @@ class TrexTUIDashBoard(TrexTUIPanel): class TrexTUIPort(TrexTUIPanel): def __init__ (self, mng, port_id): super(TrexTUIPort, self).__init__(mng, "port {0}".format(port_id)) + self.port_id = port_id + self.port = self.mng.stateless_client.get_port(port_id) self.key_actions = OrderedDict() self.key_actions['p'] = {'action': self.action_pause, 'legend': 'pause', 'show': True} @@ -118,27 +135,41 @@ class TrexTUIPort(TrexTUIPanel): self.key_actions['+'] = {'action': self.action_raise, 'legend': 'up 5%', 'show': True} self.key_actions['-'] = {'action': self.action_lower, 'legend': 'low 5%', 'show': True} + def show (self): + stats = self.stateless_client.cmd_stats([self.port_id], trex_stats.ALL_STATS_OPTS) # print stats to screen for stat_type, stat_data in stats.iteritems(): text_tables.print_table_with_header(stat_data.text_table, stat_type) def get_key_actions (self): - return self.key_actions + + allowed = {} + + if self.port.state == self.port.STATE_TX: + allowed['p'] = self.key_actions['p'] + allowed['+'] = self.key_actions['+'] + allowed['-'] = self.key_actions['-'] + + elif self.port.state == self.port.STATE_PAUSE: + allowed['r'] = self.key_actions['r'] + + + return allowed # actions def action_pause (self): rc = self.stateless_client.pause_traffic([self.port_id]) if rc.good(): - return "paused traffic on port {0}".format(self.port_id) + return "port {0}: paused traffic".format(self.port_id) else: return "" def action_resume (self): rc = self.stateless_client.resume_traffic([self.port_id]) if rc.good(): - return "resumed traffic on port {0}".format(self.port_id) + return "port {0}: resumed traffic".format(self.port_id) else: return "" @@ -147,7 +178,7 @@ class TrexTUIPort(TrexTUIPanel): rc = self.stateless_client.update_traffic(mul, [self.port_id]) if rc.good(): - return "raised B/W by 5% on port {0}".format(self.port_id) + return "port {0}: raised B/W by 5%".format(self.port_id) else: return "" @@ -156,7 +187,7 @@ class TrexTUIPort(TrexTUIPanel): rc = self.stateless_client.update_traffic(mul, [self.port_id]) if rc.good(): - return "lowered B/W by 5% on port {0}".format(self.port_id) + return "port {0}: lowered B/W by 5%".format(self.port_id) else: return "" @@ -185,6 +216,7 @@ class TrexTUIPanelManager(): self.tui = tui self.stateless_client = tui.stateless_client self.acquired_ports = self.stateless_client.get_acquired_ports() + self.panels = {} self.panels['dashboard'] = TrexTUIDashBoard(self) @@ -200,10 +232,11 @@ class TrexTUIPanelManager(): # start with dashboard self.main_panel = self.panels['dashboard'] - self.generate_legend() - + # log object self.log = TrexTUILog() + self.generate_legend() + def generate_legend (self): self.legend = "{:<12}".format("browse:") @@ -215,9 +248,6 @@ class TrexTUIPanelManager(): self.legend += "'0-{0}' - port display".format(len(self.acquired_ports) - 1) - panel_actions = self.main_panel.get_key_actions() - if not panel_actions: - return self.legend += "\n{:<12}".format(self.main_panel.get_name() + ":") for k, v in self.main_panel.get_key_actions().iteritems(): @@ -230,6 +260,10 @@ class TrexTUIPanelManager(): print format_text(self.legend, 'bold') + # on window switch or turn on / off of the TUI we call this + def init (self): + self.generate_legend() + def show (self): self.main_panel.show() self.print_legend() @@ -265,11 +299,13 @@ class TrexTUIPanelManager(): def action_show_dash (self): self.main_panel = self.panels['dashboard'] + self.init() return "" def action_show_port (self, port_id): def action_show_port_x (): self.main_panel = self.panels['port {0}'.format(port_id)] + self.init() return "" return action_show_port_x @@ -309,6 +345,8 @@ class TrexTUI(): new_settings[6][termios.VTIME] = 0 # cc termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new_settings) + self.pm.init() + try: while True: self.clear_screen() -- cgit From 1355327e97e6d5ce5800fa4d6f879695922e8637 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 8 Dec 2015 11:42:59 -0500 Subject: some modifications to the tables added clear stats as well --- .../client/trex_stateless_client.py | 28 +++++++++++-- .../trex_control_plane/common/trex_stats.py | 47 +++++++++++++++++----- .../trex_control_plane/console/trex_console.py | 9 +++-- .../trex_control_plane/console/trex_tui.py | 29 ++++++++++--- 4 files changed, 91 insertions(+), 22 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 72dc5e1b..db0ed5bf 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -539,11 +539,19 @@ class Port(object): class CTRexStatelessClient(object): """docstring for CTRexStatelessClient""" + # verbose levels + VERBOSE_SILENCE = 0 + VERBOSE_REGULAR = 1 + VERBOSE_HIGH = 2 + def __init__(self, username, server="localhost", sync_port = 5050, async_port = 4500, virtual=False): super(CTRexStatelessClient, self).__init__() self.user = username self.comm_link = CTRexStatelessClient.CCommLink(server, sync_port, virtual) - self.verbose = False + + # default verbose level + self.verbose = self.VERBOSE_REGULAR + self.ports = {} self._connection_info = {"server": server, "sync_port": sync_port, @@ -571,6 +579,7 @@ class CTRexStatelessClient(object): def get_port (self, port_id): return self.ports.get(port_id, None) + ################# events handler ###################### def add_event_log (self, msg, ev_type, show = False): @@ -583,7 +592,7 @@ class CTRexStatelessClient(object): st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') self.events.append("{:<10} - {:^8} - {:}".format(st, prefix, format_text(msg, 'bold'))) - if show: + if show and self.check_verbose(self.VERBOSE_REGULAR): print format_text("\n{:^8} - {:}".format(prefix, format_text(msg, 'bold'))) @@ -850,9 +859,22 @@ class CTRexStatelessClient(object): if port_obj.is_transmitting()] def set_verbose(self, mode): - self.comm_link.set_verbose(mode) + + # on high - enable link verbose + if mode == self.VERBOSE_HIGH: + self.comm_link.set_verbose(True) + else: + self.comm_link.set_verbose(False) + self.verbose = mode + + def check_verbose (self, level): + return (self.verbose >= level) + + def get_verbose (self): + return self.verbose + ############# server actions ################ # ping server diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py index 1f9d59e3..671a0656 100755 --- a/scripts/automation/trex_control_plane/common/trex_stats.py +++ b/scripts/automation/trex_control_plane/common/trex_stats.py @@ -11,6 +11,8 @@ GLOBAL_STATS = 'g' PORT_STATS = 'p' PORT_STATUS = 'ps' ALL_STATS_OPTS = {GLOBAL_STATS, PORT_STATS, PORT_STATUS} +COMPACT = {GLOBAL_STATS, PORT_STATS} + ExportableStats = namedtuple('ExportableStats', ['raw_data', 'text_table']) @@ -54,15 +56,24 @@ class CTRexStatsGenerator(object): return_stats_data = {} per_field_stats = OrderedDict([("owner", []), - ("active", []), + ("state", []), + ("--", []), + ("opackets", []), + ("obytes", []), + ("ipackets", []), + ("ibytes", []), + ("ierrors", []), + ("oerrors", []), ("tx-bytes", []), ("rx-bytes", []), ("tx-pkts", []), ("rx-pkts", []), - ("tx-errors", []), - ("rx-errors", []), - ("tx-BW", []), - ("rx-BW", []) + ("---", []), + ("Tx bps", []), + ("Rx bps", []), + ("----", []), + ("Tx pps", []), + ("Rx pps", []) ] ) @@ -76,6 +87,9 @@ class CTRexStatsGenerator(object): stats_table = text_tables.TRexTextTable() stats_table.set_cols_align(["l"] + ["r"]*len(relevant_ports)) + stats_table.set_cols_width([10] + [20] * len(relevant_ports)) + stats_table.set_cols_dtype(['t'] + ['t'] * len(relevant_ports)) + stats_table.add_rows([[k] + v for k, v in per_field_stats.iteritems()], header=False) @@ -106,6 +120,8 @@ class CTRexStatsGenerator(object): stats_table = text_tables.TRexTextTable() stats_table.set_cols_align(["l"] + ["c"]*len(relevant_ports)) + stats_table.set_cols_width([10] + [20] * len(relevant_ports)) + stats_table.add_rows([[k] + v for k, v in per_field_status.iteritems()], header=False) @@ -250,15 +266,26 @@ class CPortStats(CTRexStats): def generate_stats(self): return {"owner": self._port_obj.user, - "active": "YES" if self._port_obj.is_active() else "NO", + "state": self._port_obj.get_port_state_name(), + "--": "", + "opackets" : self.get_rel("opackets"), + "obytes" : self.get_rel("obytes"), + "ipackets" : self.get_rel("ipackets"), + "ibytes" : self.get_rel("ibytes"), + "ierrors" : self.get_rel("ierrors"), + "oerrors" : self.get_rel("oerrors"), + "tx-bytes": self.get_rel("obytes", format = True, suffix = "B"), "rx-bytes": self.get_rel("ibytes", format = True, suffix = "B"), "tx-pkts": self.get_rel("opackets", format = True, suffix = "pkts"), "rx-pkts": self.get_rel("ipackets", format = True, suffix = "pkts"), - "tx-errors": self.get_rel("oerrors", format = True), - "rx-errors": self.get_rel("ierrors", format = True), - "tx-BW": self.get("m_total_tx_bps", format = True, suffix = "bps"), - "rx-BW": self.get("m_total_rx_bps", format = True, suffix = "bps") + + "---": "", + "Tx bps": self.get("m_total_tx_bps", format = True, suffix = "bps"), + "Rx bps": self.get("m_total_rx_bps", format = True, suffix = "bps"), + "----": "", + "Tx pps": self.get("m_total_tx_pps", format = True, suffix = "pps"), + "Rx pps": self.get("m_total_rx_pps", format = True, suffix = "pps"), } diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 8d52b2a4..9140977a 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -238,12 +238,12 @@ class TRexConsole(TRexGeneralCmd): elif line == "on": self.verbose = True - self.stateless_client.set_verbose(True) + self.stateless_client.set_verbose(self.stateless_client.VERBOSE_HIGH) print format_text("\nverbose set to on\n", 'green', 'bold') elif line == "off": self.verbose = False - self.stateless_client.set_verbose(False) + self.stateless_client.set_verbose(self.stateless_client.VERBOSE_REGULAR) print format_text("\nverbose set to off\n", 'green', 'bold') else: @@ -424,8 +424,11 @@ class TRexConsole(TRexGeneralCmd): def do_tui (self, line): '''Shows a graphical console\n''' - self.do_verbose('off') + save_verbose = self.stateless_client.get_verbose() + + self.stateless_client.set_verbose(self.stateless_client.VERBOSE_SILENCE) self.tui.show() + self.stateless_client.set_verbose(save_verbose) # quit function def do_quit(self, line): diff --git a/scripts/automation/trex_control_plane/console/trex_tui.py b/scripts/automation/trex_control_plane/console/trex_tui.py index ea271985..c44efe15 100644 --- a/scripts/automation/trex_control_plane/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/console/trex_tui.py @@ -33,16 +33,18 @@ class TrexTUIDashBoard(TrexTUIPanel): super(TrexTUIDashBoard, self).__init__(mng, "dashboard") self.key_actions = OrderedDict() - self.key_actions['p'] = {'action': self.action_pause, 'legend': 'pause', 'show': True} + + self.key_actions['c'] = {'action': self.action_clear, 'legend': 'clear', 'show': True} + self.key_actions['p'] = {'action': self.action_pause, 'legend': 'pause', 'show': True} self.key_actions['r'] = {'action': self.action_resume, 'legend': 'resume', 'show': True} - self.key_actions['+'] = {'action': self.action_raise, 'legend': 'up 5%', 'show': True} - self.key_actions['-'] = {'action': self.action_lower, 'legend': 'low 5%', 'show': True} + self.key_actions['+'] = {'action': self.action_raise, 'legend': 'up 5%', 'show': True} + self.key_actions['-'] = {'action': self.action_lower, 'legend': 'low 5%', 'show': True} self.ports = self.stateless_client.get_acquired_ports() def show (self): - stats = self.stateless_client.cmd_stats(self.ports, trex_stats.ALL_STATS_OPTS) + stats = self.stateless_client.cmd_stats(self.ports, trex_stats.COMPACT) # print stats to screen for stat_type, stat_data in stats.iteritems(): text_tables.print_table_with_header(stat_data.text_table, stat_type) @@ -51,6 +53,8 @@ class TrexTUIDashBoard(TrexTUIPanel): def get_key_actions (self): allowed = {} + allowed['c'] = self.key_actions['c'] + if len(self.stateless_client.get_transmitting_ports()) > 0: allowed['p'] = self.key_actions['p'] allowed['+'] = self.key_actions['+'] @@ -121,6 +125,11 @@ class TrexTUIDashBoard(TrexTUIPanel): return "" + def action_clear (self): + self.stateless_client.cmd_clear(self.mng.acquired_ports) + return "cleared all stats" + + # port panel class TrexTUIPort(TrexTUIPanel): def __init__ (self, mng, port_id): @@ -130,6 +139,8 @@ class TrexTUIPort(TrexTUIPanel): self.port = self.mng.stateless_client.get_port(port_id) self.key_actions = OrderedDict() + + self.key_actions['c'] = {'action': self.action_clear, 'legend': 'clear', 'show': True} self.key_actions['p'] = {'action': self.action_pause, 'legend': 'pause', 'show': True} self.key_actions['r'] = {'action': self.action_resume, 'legend': 'resume', 'show': True} self.key_actions['+'] = {'action': self.action_raise, 'legend': 'up 5%', 'show': True} @@ -138,7 +149,7 @@ class TrexTUIPort(TrexTUIPanel): def show (self): - stats = self.stateless_client.cmd_stats([self.port_id], trex_stats.ALL_STATS_OPTS) + stats = self.stateless_client.cmd_stats([self.port_id], trex_stats.COMPACT) # print stats to screen for stat_type, stat_data in stats.iteritems(): text_tables.print_table_with_header(stat_data.text_table, stat_type) @@ -147,6 +158,8 @@ class TrexTUIPort(TrexTUIPanel): allowed = {} + allowed['c'] = self.key_actions['c'] + if self.port.state == self.port.STATE_TX: allowed['p'] = self.key_actions['p'] allowed['+'] = self.key_actions['+'] @@ -191,6 +204,10 @@ class TrexTUIPort(TrexTUIPanel): else: return "" + def action_clear (self): + self.stateless_client.cmd_clear([self.port_id]) + return "port {0}: cleared stats".format(self.port_id) + # log class TrexTUILog(): def __init__ (self): @@ -239,7 +256,7 @@ class TrexTUIPanelManager(): def generate_legend (self): - self.legend = "{:<12}".format("browse:") + self.legend = "\n{:<12}".format("browse:") for k, v in self.key_actions.iteritems(): if v['show']: -- cgit From 95c2405d6373ca3c6b69efc3faf293cd41a55c76 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 9 Dec 2015 15:01:25 -0500 Subject: read only support --- .../trex_control_plane/client/trex_port.py | 410 +++++++++++++ .../client/trex_stateless_client.py | 644 ++++----------------- .../trex_control_plane/common/trex_stats.py | 16 +- .../trex_control_plane/common/trex_streams.py | 61 +- .../trex_control_plane/common/trex_types.py | 66 +++ .../trex_control_plane/console/trex_console.py | 78 ++- .../trex_control_plane/console/trex_tui.py | 36 +- 7 files changed, 745 insertions(+), 566 deletions(-) create mode 100644 scripts/automation/trex_control_plane/client/trex_port.py create mode 100644 scripts/automation/trex_control_plane/common/trex_types.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_port.py b/scripts/automation/trex_control_plane/client/trex_port.py new file mode 100644 index 00000000..68d89775 --- /dev/null +++ b/scripts/automation/trex_control_plane/client/trex_port.py @@ -0,0 +1,410 @@ + +from collections import namedtuple +from common.trex_types import * +from common import trex_stats + +# describes a single port +class Port(object): + STATE_DOWN = 0 + STATE_IDLE = 1 + STATE_STREAMS = 2 + STATE_TX = 3 + STATE_PAUSE = 4 + PortState = namedtuple('PortState', ['state_id', 'state_name']) + STATES_MAP = {STATE_DOWN: "DOWN", + STATE_IDLE: "IDLE", + STATE_STREAMS: "IDLE", + STATE_TX: "ACTIVE", + STATE_PAUSE: "PAUSE"} + + + def __init__ (self, port_id, speed, driver, user, session_id, comm_link): + self.port_id = port_id + self.state = self.STATE_IDLE + self.handler = None + self.comm_link = comm_link + self.transmit = comm_link.transmit + self.transmit_batch = comm_link.transmit_batch + self.user = user + self.session_id = session_id + self.driver = driver + self.speed = speed + self.streams = {} + self.profile = None + + self.port_stats = trex_stats.CPortStats(self) + + + def err(self, msg): + return RC_ERR("port {0} : {1}".format(self.port_id, msg)) + + def ok(self, data = "ACK"): + return RC_OK(data) + + def get_speed_bps (self): + return (self.speed * 1000 * 1000 * 1000) + + # take the port + def acquire(self, force = False): + params = {"port_id": self.port_id, + "user": self.user, + "session_id": self.session_id, + "force": force} + + command = RpcCmdData("acquire", params) + rc = self.transmit(command.method, command.params) + if rc.success: + self.handler = rc.data + return self.ok() + else: + return self.err(rc.data) + + # release the port + def release(self): + params = {"port_id": self.port_id, + "handler": self.handler} + + command = RpcCmdData("release", params) + rc = self.transmit(command.method, command.params) + self.handler = None + + if rc.success: + return self.ok() + else: + return self.err(rc.data) + + def is_acquired(self): + return (self.handler != None) + + def is_active(self): + return(self.state == self.STATE_TX ) or (self.state == self.STATE_PAUSE) + + def is_transmitting (self): + return (self.state == self.STATE_TX) + + def is_paused (self): + return (self.state == self.STATE_PAUSE) + + + def sync(self): + params = {"port_id": self.port_id} + + command = RpcCmdData("get_port_status", params) + rc = self.transmit(command.method, command.params) + if not rc.success: + return self.err(rc.data) + + # sync the port + port_state = rc.data['state'] + + if port_state == "DOWN": + self.state = self.STATE_DOWN + elif port_state == "IDLE": + self.state = self.STATE_IDLE + elif port_state == "STREAMS": + self.state = self.STATE_STREAMS + elif port_state == "TX": + self.state = self.STATE_TX + elif port_state == "PAUSE": + self.state = self.STATE_PAUSE + else: + raise Exception("port {0}: bad state received from server '{1}'".format(self.port_id, sync_data['state'])) + + return self.ok() + + + # return TRUE if write commands + def is_port_writable (self): + # operations on port can be done on state idle or state streams + return ((self.state == self.STATE_IDLE) or (self.state == self.STATE_STREAMS)) + + # add stream to the port + def add_stream (self, stream_id, stream_obj): + + if not self.is_port_writable(): + return self.err("Please stop port before attempting to add streams") + + + params = {"handler": self.handler, + "port_id": self.port_id, + "stream_id": stream_id, + "stream": stream_obj} + + rc, data = self.transmit("add_stream", params) + if not rc: + r = self.err(data) + print r.good() + + # add the stream + self.streams[stream_id] = stream_obj + + # the only valid state now + self.state = self.STATE_STREAMS + + return self.ok() + + # add multiple streams + def add_streams (self, streams_list): + batch = [] + + for stream in streams_list: + params = {"handler": self.handler, + "port_id": self.port_id, + "stream_id": stream.stream_id, + "stream": stream.stream} + + cmd = RpcCmdData('add_stream', params) + batch.append(cmd) + + rc, data = self.transmit_batch(batch) + + if not rc: + return self.err(data) + + # add the stream + for stream in streams_list: + self.streams[stream.stream_id] = stream.stream + + # the only valid state now + self.state = self.STATE_STREAMS + + return self.ok() + + # remove stream from port + def remove_stream (self, stream_id): + + if not stream_id in self.streams: + return self.err("stream {0} does not exists".format(stream_id)) + + params = {"handler": self.handler, + "port_id": self.port_id, + "stream_id": stream_id} + + + rc, data = self.transmit("remove_stream", params) + if not rc: + return self.err(data) + + self.streams[stream_id] = None + + self.state = self.STATE_STREAMS if len(self.streams > 0) else self.STATE_IDLE + + return self.ok() + + # remove all the streams + def remove_all_streams (self): + + params = {"handler": self.handler, + "port_id": self.port_id} + + rc, data = self.transmit("remove_all_streams", params) + if not rc: + return self.err(data) + + self.streams = {} + + self.state = self.STATE_IDLE + + return self.ok() + + # get a specific stream + def get_stream (self, stream_id): + if stream_id in self.streams: + return self.streams[stream_id] + else: + return None + + def get_all_streams (self): + return self.streams + + # start traffic + def start (self, mul, duration): + if self.state == self.STATE_DOWN: + return self.err("Unable to start traffic - port is down") + + if self.state == self.STATE_IDLE: + return self.err("Unable to start traffic - no streams attached to port") + + if self.state == self.STATE_TX: + return self.err("Unable to start traffic - port is already transmitting") + + params = {"handler": self.handler, + "port_id": self.port_id, + "mul": mul, + "duration": duration} + + rc, data = self.transmit("start_traffic", params) + if not rc: + return self.err(data) + + self.state = self.STATE_TX + + return self.ok() + + # stop traffic + # with force ignores the cached state and sends the command + def stop (self, force = False): + + if (not force) and (self.state != self.STATE_TX) and (self.state != self.STATE_PAUSE): + return self.err("port is not transmitting") + + params = {"handler": self.handler, + "port_id": self.port_id} + + rc, data = self.transmit("stop_traffic", params) + if not rc: + return self.err(data) + + # only valid state after stop + self.state = self.STATE_STREAMS + + return self.ok() + + def pause (self): + + if (self.state != self.STATE_TX) : + return self.err("port is not transmitting") + + params = {"handler": self.handler, + "port_id": self.port_id} + + rc, data = self.transmit("pause_traffic", params) + if not rc: + return self.err(data) + + # only valid state after stop + self.state = self.STATE_PAUSE + + return self.ok() + + + def resume (self): + + if (self.state != self.STATE_PAUSE) : + return self.err("port is not in pause mode") + + params = {"handler": self.handler, + "port_id": self.port_id} + + rc, data = self.transmit("resume_traffic", params) + if not rc: + return self.err(data) + + # only valid state after stop + self.state = self.STATE_TX + + return self.ok() + + + def update (self, mul): + if (self.state != self.STATE_TX) : + return self.err("port is not transmitting") + + params = {"handler": self.handler, + "port_id": self.port_id, + "mul": mul} + + rc, data = self.transmit("update_traffic", params) + if not rc: + return self.err(data) + + return self.ok() + + + def validate (self): + + if (self.state == self.STATE_DOWN): + return self.err("port is down") + + if (self.state == self.STATE_IDLE): + return self.err("no streams attached to port") + + params = {"handler": self.handler, + "port_id": self.port_id} + + rc, data = self.transmit("validate", params) + if not rc: + return self.err(data) + + self.profile = data + + return self.ok() + + def get_profile (self): + return self.profile + + + def print_profile (self, mult, duration): + if not self.get_profile(): + return + + rate = self.get_profile()['rate'] + graph = self.get_profile()['graph'] + + print format_text("Profile Map Per Port\n", 'underline', 'bold') + + factor = mult_to_factor(mult, rate['max_bps'], rate['max_pps'], rate['max_line_util']) + + print "Profile max BPS (base / req): {:^12} / {:^12}".format(format_num(rate['max_bps'], suffix = "bps"), + format_num(rate['max_bps'] * factor, suffix = "bps")) + + print "Profile max PPS (base / req): {:^12} / {:^12}".format(format_num(rate['max_pps'], suffix = "pps"), + format_num(rate['max_pps'] * factor, suffix = "pps"),) + + print "Profile line util. (base / req): {:^12} / {:^12}".format(format_percentage(rate['max_line_util'] * 100), + format_percentage(rate['max_line_util'] * factor * 100)) + + + # duration + exp_time_base_sec = graph['expected_duration'] / (1000 * 1000) + exp_time_factor_sec = exp_time_base_sec / factor + + # user configured a duration + if duration > 0: + if exp_time_factor_sec > 0: + exp_time_factor_sec = min(exp_time_factor_sec, duration) + else: + exp_time_factor_sec = duration + + + print "Duration (base / req): {:^12} / {:^12}".format(format_time(exp_time_base_sec), + format_time(exp_time_factor_sec)) + print "\n" + + + def get_port_state_name(self): + return self.STATES_MAP.get(self.state, "Unknown") + + ################# stats handler ###################### + def generate_port_stats(self): + return self.port_stats.generate_stats() + pass + + def generate_port_status(self): + return {"port-type": self.driver, + "maximum": "{speed} Gb/s".format(speed=self.speed), + "port-status": self.get_port_state_name() + } + + def clear_stats(self): + return self.port_stats.clear_stats() + + + ################# events handler ###################### + def async_event_port_stopped (self): + self.state = self.STATE_STREAMS + + + def async_event_port_started (self): + self.state = self.STATE_TX + + + def async_event_port_paused (self): + self.state = self.STATE_PAUSE + + + def async_event_port_resumed (self): + self.state = self.STATE_TX + + def async_event_forced_acquired (self): + self.handler = None diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index db0ed5bf..43ebea9d 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -19,68 +19,12 @@ from client_utils import parsing_opts, text_tables import time import datetime import re +import random +from trex_port import Port +from common.trex_types import * from trex_async_client import CTRexAsyncClient -RpcCmdData = namedtuple('RpcCmdData', ['method', 'params']) - -class RpcResponseStatus(namedtuple('RpcResponseStatus', ['success', 'id', 'msg'])): - __slots__ = () - def __str__(self): - return "{id:^3} - {msg} ({stat})".format(id=self.id, - msg=self.msg, - stat="success" if self.success else "fail") - -# simple class to represent complex return value -class RC(): - - def __init__ (self, rc = None, data = None): - self.rc_list = [] - - if (rc != None) and (data != None): - tuple_rc = namedtuple('RC', ['rc', 'data']) - self.rc_list.append(tuple_rc(rc, data)) - - def add (self, rc): - self.rc_list += rc.rc_list - - def good (self): - return all([x.rc for x in self.rc_list]) - - def bad (self): - return not self.good() - - def data (self): - return [x.data if x.rc else "" for x in self.rc_list] - - def err (self): - return [x.data if not x.rc else "" for x in self.rc_list] - - def annotate (self, desc = None): - if desc: - print format_text('\n{:<60}'.format(desc), 'bold'), - - if self.bad(): - # print all the errors - print "" - for x in self.rc_list: - if not x.rc: - print format_text("\n{0}".format(x.data), 'bold') - - print "" - print format_text("[FAILED]\n", 'red', 'bold') - - - else: - print format_text("[SUCCESS]\n", 'green', 'bold') - - -def RC_OK(data = ""): - return RC(True, data) -def RC_ERR (err): - return RC(False, err) - -LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) ########## utlity ############ def mult_to_factor (mult, max_bps, max_pps, line_util): @@ -98,444 +42,6 @@ def mult_to_factor (mult, max_bps, max_pps, line_util): -# describes a stream DB -class CStreamsDB(object): - - def __init__(self): - self.stream_packs = {} - - def load_yaml_file(self, filename): - - stream_pack_name = filename - if stream_pack_name in self.get_loaded_streams_names(): - self.remove_stream_packs(stream_pack_name) - - stream_list = CStreamList() - loaded_obj = stream_list.load_yaml(filename) - - try: - compiled_streams = stream_list.compile_streams() - rc = self.load_streams(stream_pack_name, - LoadedStreamList(loaded_obj, - [StreamPack(v.stream_id, v.stream.dump()) - for k, v in compiled_streams.items()])) - - except Exception as e: - return None - - return self.get_stream_pack(stream_pack_name) - - def load_streams(self, name, LoadedStreamList_obj): - if name in self.stream_packs: - return False - else: - self.stream_packs[name] = LoadedStreamList_obj - return True - - def remove_stream_packs(self, *names): - removed_streams = [] - for name in names: - removed = self.stream_packs.pop(name) - if removed: - removed_streams.append(name) - return removed_streams - - def clear(self): - self.stream_packs.clear() - - def get_loaded_streams_names(self): - return self.stream_packs.keys() - - def stream_pack_exists (self, name): - return name in self.get_loaded_streams_names() - - def get_stream_pack(self, name): - if not self.stream_pack_exists(name): - return None - else: - return self.stream_packs.get(name) - - -# describes a single port -class Port(object): - STATE_DOWN = 0 - STATE_IDLE = 1 - STATE_STREAMS = 2 - STATE_TX = 3 - STATE_PAUSE = 4 - PortState = namedtuple('PortState', ['state_id', 'state_name']) - STATES_MAP = {STATE_DOWN: "DOWN", - STATE_IDLE: "IDLE", - STATE_STREAMS: "STREAMS", - STATE_TX: "ACTIVE", - STATE_PAUSE: "PAUSE"} - - - def __init__ (self, port_id, speed, driver, user, comm_link): - self.port_id = port_id - self.state = self.STATE_IDLE - self.handler = None - self.comm_link = comm_link - self.transmit = comm_link.transmit - self.transmit_batch = comm_link.transmit_batch - self.user = user - self.driver = driver - self.speed = speed - self.streams = {} - self.profile = None - - self.port_stats = trex_stats.CPortStats(self) - - def err(self, msg): - return RC_ERR("port {0} : {1}".format(self.port_id, msg)) - - def ok(self, data = "ACK"): - return RC_OK(data) - - def get_speed_bps (self): - return (self.speed * 1000 * 1000 * 1000) - - # take the port - def acquire(self, force = False): - params = {"port_id": self.port_id, - "user": self.user, - "force": force} - - command = RpcCmdData("acquire", params) - rc = self.transmit(command.method, command.params) - if rc.success: - self.handler = rc.data - return self.ok() - else: - return self.err(rc.data) - - # release the port - def release(self): - params = {"port_id": self.port_id, - "handler": self.handler} - - command = RpcCmdData("release", params) - rc = self.transmit(command.method, command.params) - if rc.success: - self.handler = rc.data - return self.ok() - else: - return self.err(rc.data) - - def is_acquired(self): - return (self.handler != None) - - def is_active(self): - return(self.state == self.STATE_TX ) or (self.state == self.STATE_PAUSE) - - def is_transmitting (self): - return (self.state == self.STATE_TX) - - def is_paused (self): - return (self.state == self.STATE_PAUSE) - - - def sync(self, sync_data): - self.handler = sync_data['handler'] - port_state = sync_data['state'].upper() - if port_state == "DOWN": - self.state = self.STATE_DOWN - elif port_state == "IDLE": - self.state = self.STATE_IDLE - elif port_state == "STREAMS": - self.state = self.STATE_STREAMS - elif port_state == "TX": - self.state = self.STATE_TX - elif port_state == "PAUSE": - self.state = self.STATE_PAUSE - else: - raise Exception("port {0}: bad state received from server '{1}'".format(self.port_id, sync_data['state'])) - - return self.ok() - - - # return TRUE if write commands - def is_port_writable (self): - # operations on port can be done on state idle or state streams - return ((self.state == self.STATE_IDLE) or (self.state == self.STATE_STREAMS)) - - # add stream to the port - def add_stream (self, stream_id, stream_obj): - - if not self.is_port_writable(): - return self.err("Please stop port before attempting to add streams") - - - params = {"handler": self.handler, - "port_id": self.port_id, - "stream_id": stream_id, - "stream": stream_obj} - - rc, data = self.transmit("add_stream", params) - if not rc: - r = self.err(data) - print r.good() - - # add the stream - self.streams[stream_id] = stream_obj - - # the only valid state now - self.state = self.STATE_STREAMS - - return self.ok() - - # add multiple streams - def add_streams (self, streams_list): - batch = [] - - for stream in streams_list: - params = {"handler": self.handler, - "port_id": self.port_id, - "stream_id": stream.stream_id, - "stream": stream.stream} - - cmd = RpcCmdData('add_stream', params) - batch.append(cmd) - - rc, data = self.transmit_batch(batch) - - if not rc: - return self.err(data) - - # add the stream - for stream in streams_list: - self.streams[stream.stream_id] = stream.stream - - # the only valid state now - self.state = self.STATE_STREAMS - - return self.ok() - - # remove stream from port - def remove_stream (self, stream_id): - - if not stream_id in self.streams: - return self.err("stream {0} does not exists".format(stream_id)) - - params = {"handler": self.handler, - "port_id": self.port_id, - "stream_id": stream_id} - - - rc, data = self.transmit("remove_stream", params) - if not rc: - return self.err(data) - - self.streams[stream_id] = None - - self.state = self.STATE_STREAMS if len(self.streams > 0) else self.STATE_IDLE - - return self.ok() - - # remove all the streams - def remove_all_streams (self): - - params = {"handler": self.handler, - "port_id": self.port_id} - - rc, data = self.transmit("remove_all_streams", params) - if not rc: - return self.err(data) - - self.streams = {} - - self.state = self.STATE_IDLE - - return self.ok() - - # get a specific stream - def get_stream (self, stream_id): - if stream_id in self.streams: - return self.streams[stream_id] - else: - return None - - def get_all_streams (self): - return self.streams - - # start traffic - def start (self, mul, duration): - if self.state == self.STATE_DOWN: - return self.err("Unable to start traffic - port is down") - - if self.state == self.STATE_IDLE: - return self.err("Unable to start traffic - no streams attached to port") - - if self.state == self.STATE_TX: - return self.err("Unable to start traffic - port is already transmitting") - - params = {"handler": self.handler, - "port_id": self.port_id, - "mul": mul, - "duration": duration} - - rc, data = self.transmit("start_traffic", params) - if not rc: - return self.err(data) - - self.state = self.STATE_TX - - return self.ok() - - # stop traffic - # with force ignores the cached state and sends the command - def stop (self, force = False): - - if (not force) and (self.state != self.STATE_TX) and (self.state != self.STATE_PAUSE): - return self.err("port is not transmitting") - - params = {"handler": self.handler, - "port_id": self.port_id} - - rc, data = self.transmit("stop_traffic", params) - if not rc: - return self.err(data) - - # only valid state after stop - self.state = self.STATE_STREAMS - - return self.ok() - - def pause (self): - - if (self.state != self.STATE_TX) : - return self.err("port is not transmitting") - - params = {"handler": self.handler, - "port_id": self.port_id} - - rc, data = self.transmit("pause_traffic", params) - if not rc: - return self.err(data) - - # only valid state after stop - self.state = self.STATE_PAUSE - - return self.ok() - - - def resume (self): - - if (self.state != self.STATE_PAUSE) : - return self.err("port is not in pause mode") - - params = {"handler": self.handler, - "port_id": self.port_id} - - rc, data = self.transmit("resume_traffic", params) - if not rc: - return self.err(data) - - # only valid state after stop - self.state = self.STATE_TX - - return self.ok() - - - def update (self, mul): - if (self.state != self.STATE_TX) : - return self.err("port is not transmitting") - - params = {"handler": self.handler, - "port_id": self.port_id, - "mul": mul} - - rc, data = self.transmit("update_traffic", params) - if not rc: - return self.err(data) - - return self.ok() - - - def validate (self): - - if (self.state == self.STATE_DOWN): - return self.err("port is down") - - if (self.state == self.STATE_IDLE): - return self.err("no streams attached to port") - - params = {"handler": self.handler, - "port_id": self.port_id} - - rc, data = self.transmit("validate", params) - if not rc: - return self.err(data) - - self.profile = data - - return self.ok() - - def get_profile (self): - return self.profile - - - def print_profile (self, mult, duration): - if not self.get_profile(): - return - - rate = self.get_profile()['rate'] - graph = self.get_profile()['graph'] - - print format_text("Profile Map Per Port\n", 'underline', 'bold') - - factor = mult_to_factor(mult, rate['max_bps'], rate['max_pps'], rate['max_line_util']) - - print "Profile max BPS (base / req): {:^12} / {:^12}".format(format_num(rate['max_bps'], suffix = "bps"), - format_num(rate['max_bps'] * factor, suffix = "bps")) - - print "Profile max PPS (base / req): {:^12} / {:^12}".format(format_num(rate['max_pps'], suffix = "pps"), - format_num(rate['max_pps'] * factor, suffix = "pps"),) - - print "Profile line util. (base / req): {:^12} / {:^12}".format(format_percentage(rate['max_line_util'] * 100), - format_percentage(rate['max_line_util'] * factor * 100)) - - - # duration - exp_time_base_sec = graph['expected_duration'] / (1000 * 1000) - exp_time_factor_sec = exp_time_base_sec / factor - - # user configured a duration - if duration > 0: - if exp_time_factor_sec > 0: - exp_time_factor_sec = min(exp_time_factor_sec, duration) - else: - exp_time_factor_sec = duration - - - print "Duration (base / req): {:^12} / {:^12}".format(format_time(exp_time_base_sec), - format_time(exp_time_factor_sec)) - print "\n" - - - def get_port_state_name(self): - return self.STATES_MAP.get(self.state, "Unknown") - - ################# stats handler ###################### - def generate_port_stats(self): - return self.port_stats.generate_stats() - pass - - def generate_port_status(self): - return {"port-type": self.driver, - "maximum": "{speed} Gb/s".format(speed=self.speed), - "port-status": self.get_port_state_name() - } - - def clear_stats(self): - return self.port_stats.clear_stats() - - - ################# events handler ###################### - def async_event_port_stopped (self): - self.state = self.STATE_STREAMS - - class CTRexStatelessClient(object): """docstring for CTRexStatelessClient""" @@ -546,7 +52,10 @@ class CTRexStatelessClient(object): def __init__(self, username, server="localhost", sync_port = 5050, async_port = 4500, virtual=False): super(CTRexStatelessClient, self).__init__() + self.user = username + self.session_id = random.getrandbits(32) + self.comm_link = CTRexStatelessClient.CCommLink(server, sync_port, virtual) # default verbose level @@ -571,15 +80,24 @@ class CTRexStatelessClient(object): self.events = [] + + self.read_only = False self.connected = False + # when the client gets out + def shutdown (self): + self.release(self.get_acquired_ports()) + # returns the port object def get_port (self, port_id): return self.ports.get(port_id, None) + def get_server (self): + return self.comm_link.get_server() + ################# events handler ###################### def add_event_log (self, msg, ev_type, show = False): @@ -634,6 +152,7 @@ class CTRexStatelessClient(object): if (type == 0): port_id = int(data['port_id']) ev = "Port {0} has started".format(port_id) + self.async_event_port_started(port_id) # port stopped elif (type == 1): @@ -644,14 +163,24 @@ class CTRexStatelessClient(object): self.async_event_port_stopped(port_id) - # server stopped + # port paused elif (type == 2): - ev = "Server has stopped" - self.async_event_server_stopped() - show_event = True + port_id = int(data['port_id']) + ev = "Port {0} has paused".format(port_id) - # port finished traffic + # call the handler + self.async_event_port_paused(port_id) + + # port resumed elif (type == 3): + port_id = int(data['port_id']) + ev = "Port {0} has resumed".format(port_id) + + # call the handler + self.async_event_port_resumed(port_id) + + # port finished traffic + elif (type == 4): port_id = int(data['port_id']) ev = "Port {0} job done".format(port_id) @@ -659,6 +188,22 @@ class CTRexStatelessClient(object): self.async_event_port_stopped(port_id) show_event = True + # port was stolen... + elif (type == 5): + port_id = int(data['port_id']) + ev = "Port {0} was forcely taken".format(port_id) + + # call the handler + self.async_event_port_forced_acquired(port_id) + show_event = True + + # server stopped + elif (type == 100): + ev = "Server has stopped" + self.async_event_server_stopped() + show_event = True + + else: # unknown event - ignore return @@ -670,6 +215,23 @@ class CTRexStatelessClient(object): def async_event_port_stopped (self, port_id): self.ports[port_id].async_event_port_stopped() + + def async_event_port_started (self, port_id): + self.ports[port_id].async_event_port_started() + + + def async_event_port_paused (self, port_id): + self.ports[port_id].async_event_port_paused() + + + def async_event_port_resumed (self, port_id): + self.ports[port_id].async_event_port_resumed() + + + def async_event_port_forced_acquired (self, port_id): + self.ports[port_id].async_event_forced_acquired() + self.read_only = True + def async_event_server_stopped (self): self.connected = False @@ -732,7 +294,7 @@ class CTRexStatelessClient(object): ############ boot up section ################ # connection sequence - def connect(self): + def connect(self, force = False): # clear this flag self.connected = False @@ -773,21 +335,30 @@ class CTRexStatelessClient(object): speed = self.system_info['ports'][port_id]['speed'] driver = self.system_info['ports'][port_id]['driver'] - self.ports[port_id] = Port(port_id, speed, driver, self.user, self.comm_link) + self.ports[port_id] = Port(port_id, speed, driver, self.user, self.session_id, self.comm_link) - # acquire all ports - rc = self.acquire() + # sync the ports + rc = self.sync_ports() if rc.bad(): return rc - rc = self.sync_with_server() + # acquire all ports + rc = self.acquire(force = force) if rc.bad(): - return rc + # release all the succeeded ports and set as read only + self.release(self.get_acquired_ports()) + self.read_only = True + else: + self.read_only = False + self.connected = True + return rc - return RC_OK() + + def is_read_only (self): + return self.read_only def is_connected (self): return self.connected and self.comm_link.is_connected @@ -838,6 +409,9 @@ class CTRexStatelessClient(object): def get_connection_ip (self): return self.comm_link.server + def get_all_ports (self): + return [port_id for port_id, port_obj in self.ports.iteritems()] + def get_acquired_ports(self): return [port_id for port_id, port_obj in self.ports.iteritems() @@ -883,17 +457,6 @@ class CTRexStatelessClient(object): return RC(rc, info) - def sync_with_server(self, sync_streams=False): - rc, data = self.transmit("sync_user", {"user": self.user, "sync_streams": sync_streams}) - if not rc: - return RC_ERR(data) - - for port_info in data: - rc = self.ports[port_info['port_id']].sync(port_info) - if rc.bad(): - return rc - - return RC_OK() def get_global_stats(self): rc, info = self.transmit("get_global_stats") @@ -901,6 +464,16 @@ class CTRexStatelessClient(object): ########## port commands ############## + def sync_ports (self, port_id_list = None, force = False): + port_id_list = self.__ports(port_id_list) + + rc = RC() + + for port_id in port_id_list: + rc.add(self.ports[port_id].sync()) + + return rc + # acquire ports, if port_list is none - get all def acquire (self, port_id_list = None, force = False): port_id_list = self.__ports(port_id_list) @@ -919,7 +492,7 @@ class CTRexStatelessClient(object): rc = RC() for port_id in port_id_list: - rc.add(self.ports[port_id].release(force)) + rc.add(self.ports[port_id].release()) return rc @@ -1075,8 +648,8 @@ class CTRexStatelessClient(object): rc.annotate("Pinging the server on '{0}' port '{1}': ".format(self.get_connection_ip(), self.get_connection_port())) return rc - def cmd_connect(self): - rc = self.connect() + def cmd_connect(self, force): + rc = self.connect(force) rc.annotate() return rc @@ -1089,12 +662,6 @@ class CTRexStatelessClient(object): def cmd_reset(self): - # sync with the server - rc = self.sync_with_server() - rc.annotate("Syncing with the server:") - if rc.bad(): - return rc - rc = self.acquire(force = True) rc.annotate("Force acquiring all ports:") if rc.bad(): @@ -1260,6 +827,22 @@ class CTRexStatelessClient(object): ############## High Level API With Parser ################ + + def cmd_connect_line (self, line): + '''Connects to the TRex server''' + # define a parser + parser = parsing_opts.gen_parser(self, + "connect", + self.cmd_connect_line.__doc__, + parsing_opts.FORCE) + + opts = parser.parse_args(line.split()) + + if opts is None: + return RC_ERR("bad command line parameters") + + return self.cmd_connect(opts.force) + @timing def cmd_start_line (self, line): '''Start selected traffic in specified ports on TRex\n''' @@ -1551,6 +1134,9 @@ class CTRexStatelessClient(object): else: return True + def get_server (self): + return self.server + def set_verbose(self, mode): self.verbose = mode return self.rpc_link.set_verbose(mode) diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py index 671a0656..2f6ea38d 100755 --- a/scripts/automation/trex_control_plane/common/trex_stats.py +++ b/scripts/automation/trex_control_plane/common/trex_stats.py @@ -5,6 +5,7 @@ from common.text_opts import format_text from client.trex_async_client import CTRexAsyncStats import copy import datetime +import time import re GLOBAL_STATS = 'g' @@ -134,7 +135,8 @@ class CTRexStatsGenerator(object): # fetch owned ports ports = [port_obj for _, port_obj in self._ports_dict.iteritems() - if port_obj.is_acquired() and port_obj.port_id in port_id_list] + if port_obj.port_id in port_id_list] + # display only the first FOUR options, by design if len(ports) > 4: print format_text("[WARNING]: ", 'magenta', 'bold'), format_text("displaying up to 4 ports", 'magenta') @@ -155,7 +157,7 @@ class CTRexStats(object): def __init__(self): self.reference_stats = None self.latest_stats = {} - self.last_update_ts = datetime.datetime.now() + self.last_update_ts = time.time() def __getitem__(self, item): @@ -204,13 +206,16 @@ class CTRexStats(object): def update(self, snapshot): # update - self.last_update_ts = datetime.datetime.now() - self.latest_stats = snapshot - if self.reference_stats == None: + diff_time = time.time() - self.last_update_ts + + # 3 seconds is too much - this is the new reference + if (self.reference_stats == None) or (diff_time > 3): self.reference_stats = self.latest_stats + self.last_update_ts = time.time() + def clear_stats(self): self.reference_stats = self.latest_stats @@ -225,6 +230,7 @@ class CTRexStats(object): def get_rel(self, field, format=False, suffix=""): if not field in self.latest_stats: return "N/A" + if not format: return (self.latest_stats[field] - self.reference_stats[field]) else: diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py index 89de7286..86eee1f4 100755 --- a/scripts/automation/trex_control_plane/common/trex_streams.py +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -10,6 +10,7 @@ import copy import os StreamPack = namedtuple('StreamPack', ['stream_id', 'stream']) +LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) class CStreamList(object): @@ -254,5 +255,61 @@ class CStream(object): raise RuntimeError("CStream object isn't loaded with data. Use 'load_data' method.") -if __name__ == "__main__": - pass + +# describes a stream DB +class CStreamsDB(object): + + def __init__(self): + self.stream_packs = {} + + def load_yaml_file(self, filename): + + stream_pack_name = filename + if stream_pack_name in self.get_loaded_streams_names(): + self.remove_stream_packs(stream_pack_name) + + stream_list = CStreamList() + loaded_obj = stream_list.load_yaml(filename) + + try: + compiled_streams = stream_list.compile_streams() + rc = self.load_streams(stream_pack_name, + LoadedStreamList(loaded_obj, + [StreamPack(v.stream_id, v.stream.dump()) + for k, v in compiled_streams.items()])) + + except Exception as e: + return None + + return self.get_stream_pack(stream_pack_name) + + def load_streams(self, name, LoadedStreamList_obj): + if name in self.stream_packs: + return False + else: + self.stream_packs[name] = LoadedStreamList_obj + return True + + def remove_stream_packs(self, *names): + removed_streams = [] + for name in names: + removed = self.stream_packs.pop(name) + if removed: + removed_streams.append(name) + return removed_streams + + def clear(self): + self.stream_packs.clear() + + def get_loaded_streams_names(self): + return self.stream_packs.keys() + + def stream_pack_exists (self, name): + return name in self.get_loaded_streams_names() + + def get_stream_pack(self, name): + if not self.stream_pack_exists(name): + return None + else: + return self.stream_packs.get(name) + diff --git a/scripts/automation/trex_control_plane/common/trex_types.py b/scripts/automation/trex_control_plane/common/trex_types.py new file mode 100644 index 00000000..3de36e4c --- /dev/null +++ b/scripts/automation/trex_control_plane/common/trex_types.py @@ -0,0 +1,66 @@ + +from collections import namedtuple +from common.text_opts import * + +RpcCmdData = namedtuple('RpcCmdData', ['method', 'params']) + +class RpcResponseStatus(namedtuple('RpcResponseStatus', ['success', 'id', 'msg'])): + __slots__ = () + def __str__(self): + return "{id:^3} - {msg} ({stat})".format(id=self.id, + msg=self.msg, + stat="success" if self.success else "fail") + +# simple class to represent complex return value +class RC(): + + def __init__ (self, rc = None, data = None): + self.rc_list = [] + + if (rc != None) and (data != None): + tuple_rc = namedtuple('RC', ['rc', 'data']) + self.rc_list.append(tuple_rc(rc, data)) + + def add (self, rc): + self.rc_list += rc.rc_list + + def good (self): + return all([x.rc for x in self.rc_list]) + + def bad (self): + return not self.good() + + def data (self): + return [x.data if x.rc else "" for x in self.rc_list] + + def err (self): + return [x.data if not x.rc else "" for x in self.rc_list] + + def annotate (self, desc = None, show_status = True): + if desc: + print format_text('\n{:<60}'.format(desc), 'bold'), + else: + print "" + + if self.bad(): + # print all the errors + print "" + for x in self.rc_list: + if not x.rc: + print format_text("\n{0}".format(x.data), 'bold') + + print "" + if show_status: + print format_text("[FAILED]\n", 'red', 'bold') + + + else: + if show_status: + print format_text("[SUCCESS]\n", 'green', 'bold') + + +def RC_OK(data = ""): + return RC(True, data) +def RC_ERR (err): + return RC(False, err) + diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 9140977a..495e1c22 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -130,12 +130,17 @@ class TRexConsole(TRexGeneralCmd): ################### internal section ######################## + def verify_connected(f): @wraps(f) def wrap(*args): inst = args[0] + func_name = f.__name__ + if func_name.startswith("do_"): + func_name = func_name[3:] + if not inst.stateless_client.is_connected(): - print format_text("\nNot connected to server\n", 'bold') + print format_text("\n'{0}' cannot be executed on offline mode\n".format(func_name), 'bold') return ret = f(*args) @@ -143,9 +148,32 @@ class TRexConsole(TRexGeneralCmd): return wrap + # TODO: remove this ugly duplication + def verify_connected_and_rw (f): + @wraps(f) + def wrap(*args): + inst = args[0] + func_name = f.__name__ + if func_name.startswith("do_"): + func_name = func_name[3:] + + if not inst.stateless_client.is_connected(): + print format_text("\n'{0}' cannot be executed on offline mode\n".format(func_name), 'bold') + return + + if inst.stateless_client.is_read_only(): + print format_text("\n'{0}' cannot be executed on read only mode\n".format(func_name), 'bold') + return + + ret = f(*args) + return ret + + return wrap + + def get_console_identifier(self): return "{context}_{server}".format(context=self.__class__.__name__, - server=self.stateless_client.get_system_info()['hostname']) + server=self.stateless_client.get_server()) def register_main_console_methods(self): main_names = set(self.trex_console.get_names()).difference(set(dir(self.__class__))) @@ -156,11 +184,17 @@ class TRexConsole(TRexGeneralCmd): def postcmd(self, stop, line): - if self.stateless_client.is_connected(): - self.prompt = "TRex > " - else: - self.supported_rpc = None + if not self.stateless_client.is_connected(): self.prompt = "TRex (offline) > " + self.supported_rpc = None + return stop + + if self.stateless_client.is_read_only(): + self.prompt = "TRex (read only) > " + return stop + + + self.prompt = "TRex > " return stop @@ -287,7 +321,7 @@ class TRexConsole(TRexGeneralCmd): def do_connect (self, line): '''Connects to the server\n''' - rc = self.stateless_client.cmd_connect() + rc = self.stateless_client.cmd_connect_line(line) if rc.bad(): return @@ -314,7 +348,7 @@ class TRexConsole(TRexGeneralCmd): if (l > 2) and (s[l - 2] in file_flags): return TRexConsole.tree_autocomplete(s[l - 1]) - @verify_connected + @verify_connected_and_rw def do_start(self, line): '''Start selected traffic in specified port(s) on TRex\n''' @@ -325,7 +359,7 @@ class TRexConsole(TRexGeneralCmd): self.do_start("-h") ############# stop - @verify_connected + @verify_connected_and_rw def do_stop(self, line): '''stops port(s) transmitting traffic\n''' @@ -335,7 +369,7 @@ class TRexConsole(TRexGeneralCmd): self.do_stop("-h") ############# update - @verify_connected + @verify_connected_and_rw def do_update(self, line): '''update speed of port(s)currently transmitting traffic\n''' @@ -345,14 +379,14 @@ class TRexConsole(TRexGeneralCmd): self.do_update("-h") ############# pause - @verify_connected + @verify_connected_and_rw def do_pause(self, line): '''pause port(s) transmitting traffic\n''' self.stateless_client.cmd_pause_line(line) ############# resume - @verify_connected + @verify_connected_and_rw def do_resume(self, line): '''resume port(s) transmitting traffic\n''' @@ -361,7 +395,7 @@ class TRexConsole(TRexGeneralCmd): ########## reset - @verify_connected + @verify_connected_and_rw def do_reset (self, line): '''force stop all ports\n''' self.stateless_client.cmd_reset_line(line) @@ -375,6 +409,7 @@ class TRexConsole(TRexGeneralCmd): self.stateless_client.cmd_validate_line(line) + @verify_connected def do_stats(self, line): '''Fetch statistics from TRex server by port\n''' self.stateless_client.cmd_stats_line(line) @@ -383,6 +418,7 @@ class TRexConsole(TRexGeneralCmd): def help_stats(self): self.do_stats("-h") + @verify_connected def do_clear(self, line): '''Clear cached local statistics\n''' self.stateless_client.cmd_clear_line(line) @@ -529,9 +565,17 @@ def main(): # Stateless client connection stateless_client = CTRexStatelessClient(options.user, options.server, options.port, options.pub) - rc = stateless_client.cmd_connect() + + print "\nlogged as {0}".format(format_text(options.user, 'bold')) + rc = stateless_client.connect() + + # error can be either no able to connect or a read only if rc.bad(): - return + if not stateless_client.is_connected(): + rc.annotate() + else: + rc.annotate(show_status = False) + if options.batch: cont = stateless_client.run_script_file(options.batch[0]) @@ -544,7 +588,9 @@ def main(): console.cmdloop() except KeyboardInterrupt as e: print "\n\n*** Caught Ctrl + C... Exiting...\n\n" - return + + finally: + stateless_client.shutdown() if __name__ == '__main__': main() diff --git a/scripts/automation/trex_control_plane/console/trex_tui.py b/scripts/automation/trex_control_plane/console/trex_tui.py index c44efe15..3ddf7a7f 100644 --- a/scripts/automation/trex_control_plane/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/console/trex_tui.py @@ -40,7 +40,7 @@ class TrexTUIDashBoard(TrexTUIPanel): self.key_actions['+'] = {'action': self.action_raise, 'legend': 'up 5%', 'show': True} self.key_actions['-'] = {'action': self.action_lower, 'legend': 'low 5%', 'show': True} - self.ports = self.stateless_client.get_acquired_ports() + self.ports = self.stateless_client.get_all_ports() def show (self): @@ -55,6 +55,10 @@ class TrexTUIDashBoard(TrexTUIPanel): allowed['c'] = self.key_actions['c'] + # thats it for read only + if self.stateless_client.is_read_only(): + return allowed + if len(self.stateless_client.get_transmitting_ports()) > 0: allowed['p'] = self.key_actions['p'] allowed['+'] = self.key_actions['+'] @@ -69,10 +73,10 @@ class TrexTUIDashBoard(TrexTUIPanel): ######### actions def action_pause (self): - rc = self.stateless_client.pause_traffic(self.mng.acquired_ports) + rc = self.stateless_client.pause_traffic(self.mng.ports) ports_succeeded = [] - for rc_single, port_id in zip(rc.rc_list, self.mng.acquired_ports): + for rc_single, port_id in zip(rc.rc_list, self.mng.ports): if rc_single.rc: ports_succeeded.append(port_id) @@ -83,10 +87,10 @@ class TrexTUIDashBoard(TrexTUIPanel): def action_resume (self): - rc = self.stateless_client.resume_traffic(self.mng.acquired_ports) + rc = self.stateless_client.resume_traffic(self.mng.ports) ports_succeeded = [] - for rc_single, port_id in zip(rc.rc_list, self.mng.acquired_ports): + for rc_single, port_id in zip(rc.rc_list, self.mng.ports): if rc_single.rc: ports_succeeded.append(port_id) @@ -98,10 +102,10 @@ class TrexTUIDashBoard(TrexTUIPanel): def action_raise (self): mul = {'type': 'percentage', 'value': 5, 'op': 'add'} - rc = self.stateless_client.update_traffic(mul, self.mng.acquired_ports) + rc = self.stateless_client.update_traffic(mul, self.mng.ports) ports_succeeded = [] - for rc_single, port_id in zip(rc.rc_list, self.mng.acquired_ports): + for rc_single, port_id in zip(rc.rc_list, self.mng.ports): if rc_single.rc: ports_succeeded.append(port_id) @@ -112,10 +116,10 @@ class TrexTUIDashBoard(TrexTUIPanel): def action_lower (self): mul = {'type': 'percentage', 'value': 5, 'op': 'sub'} - rc = self.stateless_client.update_traffic(mul, self.mng.acquired_ports) + rc = self.stateless_client.update_traffic(mul, self.mng.ports) ports_succeeded = [] - for rc_single, port_id in zip(rc.rc_list, self.mng.acquired_ports): + for rc_single, port_id in zip(rc.rc_list, self.mng.ports): if rc_single.rc: ports_succeeded.append(port_id) @@ -126,7 +130,7 @@ class TrexTUIDashBoard(TrexTUIPanel): def action_clear (self): - self.stateless_client.cmd_clear(self.mng.acquired_ports) + self.stateless_client.cmd_clear(self.mng.ports) return "cleared all stats" @@ -148,7 +152,6 @@ class TrexTUIPort(TrexTUIPanel): def show (self): - stats = self.stateless_client.cmd_stats([self.port_id], trex_stats.COMPACT) # print stats to screen for stat_type, stat_data in stats.iteritems(): @@ -160,6 +163,10 @@ class TrexTUIPort(TrexTUIPanel): allowed['c'] = self.key_actions['c'] + # thats it for read only + if self.stateless_client.is_read_only(): + return allowed + if self.port.state == self.port.STATE_TX: allowed['p'] = self.key_actions['p'] allowed['+'] = self.key_actions['+'] @@ -232,7 +239,7 @@ class TrexTUIPanelManager(): def __init__ (self, tui): self.tui = tui self.stateless_client = tui.stateless_client - self.acquired_ports = self.stateless_client.get_acquired_ports() + self.ports = self.stateless_client.get_all_ports() self.panels = {} @@ -242,7 +249,7 @@ class TrexTUIPanelManager(): self.key_actions['q'] = {'action': self.action_quit, 'legend': 'quit', 'show': True} self.key_actions['g'] = {'action': self.action_show_dash, 'legend': 'dashboard', 'show': True} - for port_id in self.acquired_ports: + for port_id in self.ports: self.key_actions[str(port_id)] = {'action': self.action_show_port(port_id), 'legend': 'port {0}'.format(port_id), 'show': False} self.panels['port {0}'.format(port_id)] = TrexTUIPort(self, port_id) @@ -263,7 +270,7 @@ class TrexTUIPanelManager(): x = "'{0}' - {1}, ".format(k, v['legend']) self.legend += "{:}".format(x) - self.legend += "'0-{0}' - port display".format(len(self.acquired_ports) - 1) + self.legend += "'0-{0}' - port display".format(len(self.ports) - 1) self.legend += "\n{:<12}".format(self.main_panel.get_name() + ":") @@ -282,6 +289,7 @@ class TrexTUIPanelManager(): self.generate_legend() def show (self): + print self.ports self.main_panel.show() self.print_legend() self.log.show() -- cgit From 7567166ca52bd136ce08c06dcbd48c0dfd67210f Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 10 Dec 2015 03:43:55 -0500 Subject: removed session id - not necessary --- .../automation/trex_control_plane/client/trex_port.py | 4 +--- .../trex_control_plane/client/trex_stateless_client.py | 17 +++++++++++------ .../trex_control_plane/client_utils/jsonrpc_client.py | 2 +- .../trex_control_plane/console/trex_console.py | 3 ++- .../automation/trex_control_plane/console/trex_tui.py | 1 - 5 files changed, 15 insertions(+), 12 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_port.py b/scripts/automation/trex_control_plane/client/trex_port.py index 68d89775..5c5702dd 100644 --- a/scripts/automation/trex_control_plane/client/trex_port.py +++ b/scripts/automation/trex_control_plane/client/trex_port.py @@ -18,7 +18,7 @@ class Port(object): STATE_PAUSE: "PAUSE"} - def __init__ (self, port_id, speed, driver, user, session_id, comm_link): + def __init__ (self, port_id, speed, driver, user, comm_link): self.port_id = port_id self.state = self.STATE_IDLE self.handler = None @@ -26,7 +26,6 @@ class Port(object): self.transmit = comm_link.transmit self.transmit_batch = comm_link.transmit_batch self.user = user - self.session_id = session_id self.driver = driver self.speed = speed self.streams = {} @@ -48,7 +47,6 @@ class Port(object): def acquire(self, force = False): params = {"port_id": self.port_id, "user": self.user, - "session_id": self.session_id, "force": force} command = RpcCmdData("acquire", params) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 43ebea9d..6907c9c2 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -54,7 +54,6 @@ class CTRexStatelessClient(object): super(CTRexStatelessClient, self).__init__() self.user = username - self.session_id = random.getrandbits(32) self.comm_link = CTRexStatelessClient.CCommLink(server, sync_port, virtual) @@ -85,10 +84,6 @@ class CTRexStatelessClient(object): self.connected = False - # when the client gets out - def shutdown (self): - self.release(self.get_acquired_ports()) - # returns the port object def get_port (self, port_id): @@ -296,6 +291,9 @@ class CTRexStatelessClient(object): # connection sequence def connect(self, force = False): + if self.is_connected(): + self.disconnect() + # clear this flag self.connected = False @@ -335,7 +333,7 @@ class CTRexStatelessClient(object): speed = self.system_info['ports'][port_id]['speed'] driver = self.system_info['ports'][port_id]['driver'] - self.ports[port_id] = Port(port_id, speed, driver, self.user, self.session_id, self.comm_link) + self.ports[port_id] = Port(port_id, speed, driver, self.user, self.comm_link) # sync the ports @@ -365,8 +363,15 @@ class CTRexStatelessClient(object): def disconnect(self): + # release any previous acquired ports + if self.is_connected(): + self.release(self.get_acquired_ports()) + self.comm_link.disconnect() self.async_client.disconnect() + + self.connected = False + return RC_OK() diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index f55d7798..3de0bb5f 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -112,7 +112,7 @@ class JsonRpcClient(object): def invoke_rpc_method (self, method_name, params = {}): if not self.connected: - return False, "Not connected to server" + return CmdResponse(False, "Not connected to server") id, msg = self.create_jsonrpc_v2(method_name, params) diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 495e1c22..e8f90186 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -575,6 +575,7 @@ def main(): rc.annotate() else: rc.annotate(show_status = False) + print format_text("Switching to read only mode - only few commands will be available", 'bold') if options.batch: @@ -590,7 +591,7 @@ def main(): print "\n\n*** Caught Ctrl + C... Exiting...\n\n" finally: - stateless_client.shutdown() + stateless_client.disconnect() if __name__ == '__main__': main() diff --git a/scripts/automation/trex_control_plane/console/trex_tui.py b/scripts/automation/trex_control_plane/console/trex_tui.py index 3ddf7a7f..2e6be4a6 100644 --- a/scripts/automation/trex_control_plane/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/console/trex_tui.py @@ -289,7 +289,6 @@ class TrexTUIPanelManager(): self.generate_legend() def show (self): - print self.ports self.main_panel.show() self.print_legend() self.log.show() -- cgit From fc4b8bddcb4550cbf8d06e414b82a1d082d1d996 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 10 Dec 2015 06:56:17 -0500 Subject: added a "--tui" feature to the console it opens in read only mode and runs the TUI --- .../client/trex_stateless_client.py | 44 ++++++++++++++++------ .../trex_control_plane/console/trex_console.py | 40 +++++++++++--------- 2 files changed, 56 insertions(+), 28 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 6907c9c2..a2b1f6d9 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -289,7 +289,9 @@ class CTRexStatelessClient(object): ############ boot up section ################ # connection sequence - def connect(self, force = False): + + # mode can be RW - read / write, RWF - read write with force , RO - read only + def connect(self, mode = "RW"): if self.is_connected(): self.disconnect() @@ -342,17 +344,34 @@ class CTRexStatelessClient(object): return rc # acquire all ports - rc = self.acquire(force = force) - if rc.bad(): - # release all the succeeded ports and set as read only - self.release(self.get_acquired_ports()) - self.read_only = True - else: + if mode == "RW": + rc = self.acquire(force = False) + + # fallback to read only if failed + if rc.bad(): + rc.annotate(show_status = False) + print format_text("Switching to read only mode - only few commands will be available", 'bold') + + self.release(self.get_acquired_ports()) + self.read_only = True + else: + self.read_only = False + + elif mode == "RWF": + rc = self.acquire(force = True) + if rc.bad(): + return rc self.read_only = False + elif mode == "RO": + # no acquire on read only + rc = RC_OK() + self.read_only = True + + self.connected = True - return rc + return RC_OK() def is_read_only (self): @@ -653,8 +672,8 @@ class CTRexStatelessClient(object): rc.annotate("Pinging the server on '{0}' port '{1}': ".format(self.get_connection_ip(), self.get_connection_port())) return rc - def cmd_connect(self, force): - rc = self.connect(force) + def cmd_connect(self, mode = "RW"): + rc = self.connect(mode) rc.annotate() return rc @@ -846,7 +865,10 @@ class CTRexStatelessClient(object): if opts is None: return RC_ERR("bad command line parameters") - return self.cmd_connect(opts.force) + if opts.force: + rc = self.cmd_connect(mode = "RWF") + else: + rc = self.cmd_connect(mode = "RW") @timing def cmd_start_line (self, line): diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index e8f90186..0ecfce9c 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -114,14 +114,13 @@ class TRexGeneralCmd(cmd.Cmd): class TRexConsole(TRexGeneralCmd): """Trex Console""" - def __init__(self, stateless_client, acquire_all_ports=True, verbose=False): + def __init__(self, stateless_client, verbose=False): self.stateless_client = stateless_client TRexGeneralCmd.__init__(self) self.tui = trex_tui.TrexTUI(stateless_client) self.verbose = verbose - self.acquire_all_ports = acquire_all_ports self.intro = "\n-=TRex Console v{ver}=-\n".format(ver=__version__) self.intro += "\nType 'help' or '?' for supported actions\n" @@ -321,17 +320,13 @@ class TRexConsole(TRexGeneralCmd): def do_connect (self, line): '''Connects to the server\n''' - rc = self.stateless_client.cmd_connect_line(line) - if rc.bad(): - return + self.stateless_client.cmd_connect_line(line) def do_disconnect (self, line): '''Disconnect from the server\n''' - rc = self.stateless_client.cmd_disconnect() - if rc.bad(): - return + self.stateless_client.cmd_disconnect() ############### start @@ -556,6 +551,10 @@ def setParserOptions(): help = "Run the console in a batch mode with file", default = None) + parser.add_argument("-t", "--tui", dest="tui", + action="store_true", help="Starts with TUI mode", + default = False) + return parser @@ -567,17 +566,20 @@ def main(): stateless_client = CTRexStatelessClient(options.user, options.server, options.port, options.pub) print "\nlogged as {0}".format(format_text(options.user, 'bold')) - rc = stateless_client.connect() - # error can be either no able to connect or a read only + # TUI or no acquire will give us READ ONLY mode + if options.tui or not options.acquire: + rc = stateless_client.connect("RO") + else: + rc = stateless_client.connect("RW") + + # unable to connect - bye if rc.bad(): - if not stateless_client.is_connected(): - rc.annotate() - else: - rc.annotate(show_status = False) - print format_text("Switching to read only mode - only few commands will be available", 'bold') + rc.annotate() + return + # a script mode if options.batch: cont = stateless_client.run_script_file(options.batch[0]) if not cont: @@ -585,8 +587,12 @@ def main(): # console try: - console = TRexConsole(stateless_client, options.acquire, options.verbose) - console.cmdloop() + console = TRexConsole(stateless_client, options.verbose) + if options.tui: + console.do_tui("") + else: + console.cmdloop() + except KeyboardInterrupt as e: print "\n\n*** Caught Ctrl + C... Exiting...\n\n" -- cgit