From ace4f5990ef18bb3a76d042b60d7a8af02bcd357 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 18 Aug 2015 16:21:53 +0300 Subject: added status panel --- src/console/trex_status.py | 290 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100755 src/console/trex_status.py (limited to 'src/console/trex_status.py') diff --git a/src/console/trex_status.py b/src/console/trex_status.py new file mode 100755 index 00000000..a17c2589 --- /dev/null +++ b/src/console/trex_status.py @@ -0,0 +1,290 @@ +from time import sleep + +import os + +import curses +from curses import panel +import random +import collections +import operator +import datetime + +g_curses_active = False + +# +def percentage (a, total): + x = int ((float(a) / total) * 100) + return str(x) + "%" + +# panel object +class TrexStatusPanel(): + def __init__ (self, h, l, y, x, headline): + self.h = h + self.l = l + self.y = y + self.x = x + self.headline = headline + + self.win = curses.newwin(h, l, y, x) + self.win.erase() + self.win.box() + + self.win.addstr(1, 2, headline, curses.A_UNDERLINE) + self.win.refresh() + + panel.new_panel(self.win) + self.panel = panel.new_panel(self.win) + self.panel.top() + + def clear (self): + self.win.erase() + self.win.box() + self.win.addstr(1, 2, self.headline, curses.A_UNDERLINE) + + def getwin (self): + return self.win + +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 + return "NaN" + +# status object +class TrexStatus(): + def __init__ (self, stdscr, rpc_client): + self.stdscr = stdscr + self.log = [] + self.rpc_client = rpc_client + + self.get_server_info() + + def get_server_info (self): + rc, msg = self.rpc_client.get_rpc_server_status() + + if rc: + self.server_status = msg + else: + self.server_status = None + + def add_log_event (self, msg): + self.log.append("[{0}] {1}".format(str(datetime.datetime.now().time()), msg)) + + def add_panel (self, h, l, y, x, headline): + win = curses.newwin(h, l, y, x) + win.erase() + win.box() + + win.addstr(1, 2, headline) + win.refresh() + + panel.new_panel(win) + panel1 = panel.new_panel(win) + panel1.top() + + return win, panel1 + + def update_info (self): + if self.server_status == None: + return + + self.info_panel.clear() + + self.info_panel.getwin().addstr(3, 2, "{:<30} {:30}".format("Version:", self.server_status["general"]["version"])) + self.info_panel.getwin().addstr(4, 2, "{:<30} {:30}".format("Build:", + self.server_status["general"]["build_date"] + " @ " + self.server_status["general"]["build_time"] + " by " + self.server_status["general"]["version_user"])) + + self.info_panel.getwin().addstr(5, 2, "{:<30} {:30}".format("Server Uptime:", self.server_status["general"]["uptime"])) + #self.ft_panel.clear() + + #ft_section_y = 3 + #self.ft_panel.getwin().addstr(ft_section_y, 2,"General Info:", curses.A_UNDERLINE) + + #self.ft_panel.getwin().addstr(ft_section_y, 2, "{:<30} {:<30,}".format("Total Flows Opened:", ft_stats["total-opened-flows"])) + #ft_section_y = ft_section_y + 1 + #self.ft_panel.getwin().addstr(ft_section_y, 2, "{:<30} {:<30,}".format("Total Flows Closed:", ft_stats["total-closed-flows"])) + #ft_section_y = ft_section_y + 1 + #self.ft_panel.getwin().addstr(ft_section_y, 2, "{:<30} {:<30,}".format("Current Active Flows:", ft_stats["active-flows"])) + #ft_section_y = ft_section_y + 1 + #self.ft_panel.getwin().addstr(ft_section_y, 2, "{:<30} {:<30,}".format("Flow Allocation Errors:", ft_stats["allocation_err"])) + + def update_general (self, gen_stats): + + if not gen_stats: + return + + transport_info_section_y = 3 + general_info_section_y = int(self.cls_panel.h * 0.5) + + self.general_panel.clear() + + # transport layer info + self.general_panel.getwin().addstr(transport_info_section_y, 2, "{:<30} {:^10} {:^5} {:^10}".format("Total Tx Rate:", + float_to_human_readable(gen_stats["total-rx-bps"]), + "/", + float_to_human_readable(gen_stats["total-rx-pps"], suffix = "pps"))) + transport_info_section_y += 2 + + + self.general_panel.getwin().addstr(transport_info_section_y, 2, "{:<30} {:^6.2f} %".format("DP Core Util.:", gen_stats["cpu-util"])); + + transport_info_section_y += 2 + + for i in range(1, 3): + self.general_panel.getwin().addstr(transport_info_section_y, 2, "{:<30} {:^10} {:^5} {:^10}".format("Port {0} Rx:".format(i), + float_to_human_readable(gen_stats["port " + str(i)]["total-rx-bps"]), + "/", + float_to_human_readable(gen_stats["port " + str(i)]["total-rx-pps"], suffix = "pps"))) + transport_info_section_y += 1 + + + self.general_panel.getwin().addstr(general_info_section_y, 2,"General Info:", curses.A_UNDERLINE) + general_info_section_y = general_info_section_y + 2 + + self.general_panel.getwin().addstr(general_info_section_y, 2, "{:<30} {:<30}".format("VNBAR Main Process PID:", os.getppid())) + general_info_section_y = general_info_section_y + 1 + self.general_panel.getwin().addstr(general_info_section_y, 2, "{:<30} {:<30}".format("ZMQ client online at:", vnbar_ipc.VnbarIpc.get_zmq_transport_name())) + + + # v2 + def update_cls (self, pd_stats): + if not pd_stats: + return + + self.cls_panel.clear() + + section_start = 3 + section_size = (self.cls_panel.h / 2) - 5 + + for port_name, pd in sorted(pd_stats.iteritems()): + if pd == None: + continue + + # sort by bandwidth + pd = collections.OrderedDict(sorted(pd.items(), key=operator.itemgetter(1), reverse = True)) + + # restart the line index + line_index = 0 + + # headline + self.cls_panel.getwin().addstr(section_start + line_index, 2, "{0}:".format(port_name), curses.A_BOLD | curses.A_UNDERLINE) + line_index += 1 + + cls_str = "{:^45} {:^20} {:^20} {:^20}".format("Protocol Name", "Byte Count", "Packet Count", "B/W Perc.") + self.cls_panel.getwin().addstr(section_start + line_index, 2, cls_str) + line_index += 2 + + # protocols + proto_index = 0 + proto_count = len(pd) + + total_bandwidth = sum([i['bytes'] for i in pd.values()]) + + for proto_name, cnts in pd.iteritems(): + proto_str = "{:<45} {:^20,} {:^20,} {:^20}".format(proto_name, cnts['bytes'], cnts['pkts'], percentage(cnts['bytes'], total_bandwidth) ) + proto_index = proto_index + 1 + + if line_index > section_size: + self.cls_panel.getwin().addstr(section_start + line_index, 2, "<...{0} More...>".format(proto_count - proto_index), (curses.A_DIM if ((line_index % 2) == 0) else curses.A_BOLD)) + break + + self.cls_panel.getwin().addstr(section_start + line_index, 2, proto_str, (curses.A_DIM if ((line_index % 2) == 0) else curses.A_BOLD)) + + line_index += 1 + + section_start = section_start + section_size + 3 + + + def update_control (self): + self.control_panel.clear() + + self.control_panel.getwin().addstr(1, 2, "'f' - freeze, 'c' - clear stats, 'p' - ping server, 'q' - quit") + + index = 3 + + cut = len(self.log) - 4 + if cut < 0: + cut = 0 + + for l in self.log[cut:]: + self.control_panel.getwin().addstr(index, 2, l) + index += 1 + + def run (self): + try: + curses.curs_set(0) + except: + pass + + curses.use_default_colors() + self.stdscr.nodelay(1) + curses.nonl() + curses.noecho() + + self.max_y = self.stdscr.getmaxyx()[0] + self.max_x = self.stdscr.getmaxyx()[1] + + # create cls panel + self.main_panel = TrexStatusPanel(int(self.max_y * 0.8), self.max_x / 2, 0,0, "Trex Activity:") + + self.general_panel = TrexStatusPanel(int(self.max_y * 0.6), self.max_x / 2, 0, self.max_x /2, "General Statistics:") + + self.info_panel = TrexStatusPanel(int(self.max_y * 0.2), self.max_x / 2, int(self.max_y * 0.6), self.max_x /2, "Server Info:") + + self.control_panel = TrexStatusPanel(int(self.max_y * 0.2), self.max_x , int(self.max_y * 0.8), 0, "") + + panel.update_panels(); self.stdscr.refresh() + + self.update_active = True + while (True): + ch = self.stdscr.getch() + + if (ch != curses.ERR): + # stop/start status + if (ch == ord('f')): + self.update_active = not self.update_active + self.add_log_event("Update continued" if self.update_active else "Update stopped") + + elif (ch == ord('p')): + self.add_log_event("Pinging RPC server") + rc, msg = self.rpc_client.ping_rpc_server() + if rc: + self.add_log_event("Server replied") + else: + self.add_log_event("Failed to get reply") + + # c - clear stats + elif (ch == ord('c')): + self.add_log_event("Statistics cleared") + + elif (ch == ord('q')): + break + else: + self.add_log_event("Unknown key pressed {0}".format("'" + chr(ch) + "'" if chr(ch).isalpha() else "")) + + self.update_control() + self.update_info() + + panel.update_panels(); + self.stdscr.refresh() + sleep(0.2) + + +def show_trex_status_internal (stdscr, rpc_client): + trex_status = TrexStatus(stdscr, rpc_client) + trex_status.run() + +def show_trex_status (rpc_client): + + try: + curses.wrapper(show_trex_status_internal, rpc_client) + except KeyboardInterrupt: + curses.endwin() + +def cleanup (): + try: + curses.endwin() + except: + pass + -- cgit 1.2.3-korg