diff options
Diffstat (limited to 'scripts/automation/trex_control_plane/client')
-rw-r--r-- | scripts/automation/trex_control_plane/client/trex_async_client.py | 45 | ||||
-rwxr-xr-x | scripts/automation/trex_control_plane/client/trex_stateless_client.py | 237 |
2 files changed, 234 insertions, 48 deletions
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 e38c6ca7..8fdf7c9b 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: @@ -45,9 +46,12 @@ class TrexAsyncStats(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 = ""): + def get(self, field, format=False, suffix=""): if not field in self.current: return "N/A" @@ -55,17 +59,16 @@ 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 = ""): + def get_rel (self, field, format=False, suffix=""): if not field in self.current: return "N/A" 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 +77,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 +109,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 +143,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,7 +160,7 @@ class CTRexAsyncClient(): self.raw_snapshot = {} - self.stats = TrexAsyncStatsManager() + self.stats = CTRexAsyncStatsManager() self.connected = False @@ -227,7 +230,7 @@ class CTRexAsyncClient(): while self.active: try: - line = self.socket.recv_string(); + line = self.socket.recv_string() if not self.alive: self.stateless_client.on_async_alive() @@ -267,15 +270,15 @@ class CTRexAsyncClient(): def get_raw_snapshot (self): 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) + self.stateless_client.handle_async_stats_update(data) # events elif name == "trex-event": self.stateless_client.handle_async_event(type, data) else: 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 ca2ad0bb..2925e7eb 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -10,13 +10,15 @@ 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 +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 @@ -30,7 +32,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 = [] @@ -78,7 +80,6 @@ def RC_OK(data = None): def RC_ERR (err): return RC(False, err) - LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) ########## utlity ############ @@ -103,7 +104,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(): @@ -162,6 +163,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, comm_link): self.port_id = port_id @@ -176,6 +184,8 @@ class Port(object): 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)) @@ -199,7 +209,6 @@ class Port(object): else: return self.err(rc.data) - # release the port def release(self): params = {"port_id": self.port_id, @@ -492,6 +501,25 @@ class Port(object): 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 @@ -505,16 +533,22 @@ 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 - self._server_version = None + self.ports = {} + 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.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.events = [] @@ -534,6 +568,36 @@ class CTRexStatelessClient(object): if show: print format_text("\n{:^8} - {:}".format(prefix, format_text(msg, 'bold'))) + + + 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 @@ -663,12 +727,12 @@ class CTRexStatelessClient(object): return RC_ERR(data) self.server_version = data + self.global_stats.server_version = data # cache system info rc, data = self.transmit("get_system_info") if not rc: return RC_ERR(data) - self.system_info = data # cache supported commands @@ -682,7 +746,9 @@ 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.comm_link)) + + self.ports[port_id] = Port(port_id, speed, driver, self.user, self.comm_link) + # acquire all ports rc = self.acquire() @@ -747,11 +813,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) @@ -777,6 +846,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 ############## @@ -1018,11 +1090,11 @@ 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: - msg = "No active traffic on porvided ports" + msg = "No active traffic on provided ports" print format_text(msg, 'bold') return RC_ERR(msg) @@ -1031,24 +1103,34 @@ class CTRexStatelessClient(object): return rc + # clear stats + def cmd_clear(self, 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): - # find the relveant ports + # find the relevant ports 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) rc = self.pause_traffic(active_ports) rc.annotate("Pausing traffic on port(s) {0}:".format(port_id_list)) - return rc + # resume cmd def cmd_resume (self, port_id_list): @@ -1062,7 +1144,6 @@ class CTRexStatelessClient(object): rc = self.resume_traffic(active_ports) rc.annotate("Resume traffic on port(s) {0}:".format(port_id_list)) - return rc @@ -1112,12 +1193,23 @@ class CTRexStatelessClient(object): return rc - + # validate port(s) profile 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)) return rc + + # stats + def cmd_stats(self, port_id_list, stats_mask=set()): + stats_opts = trex_stats.ALL_STATS_OPTS.intersection(stats_mask) + + stats_obj = {} + for stats_type in stats_opts: + stats_obj.update(self.stats_generator.generate_single_statistic(port_id_list, stats_type)) + return stats_obj + + ############## High Level API With Parser ################ @timing def cmd_start_line (self, line): @@ -1137,14 +1229,14 @@ 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.dry: print format_text("\n*** DRY RUN ***", 'bold') 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(): @@ -1167,6 +1259,21 @@ class CTRexStatelessClient(object): return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force, opts.duration, opts.dry) @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 parameters") + + return self.cmd_resume(opts.ports) + + + @timing def cmd_stop_line (self, line): '''Stop active traffic in specified ports on TRex\n''' parser = parsing_opts.gen_parser(self, @@ -1176,10 +1283,26 @@ 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) + + @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 parameters") + + return self.cmd_pause(opts.ports) + + @timing def cmd_update_line (self, line): '''Update port(s) speed currently active\n''' @@ -1206,6 +1329,52 @@ class CTRexStatelessClient(object): 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''' + # 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") + + # determine stats mask + 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 + for stat_type, stat_data in stats.iteritems(): + text_tables.print_table_with_header(stat_data.text_table, stat_type) + + return RC_OK() + + + + @timing def cmd_pause_line (self, line): '''Pause active traffic in specified ports on TRex\n''' @@ -1269,7 +1438,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 @@ -1330,6 +1499,20 @@ class CTRexStatelessClient(object): ################################# + # ------ 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): """describes the connectivity of the stateless client method""" |