diff options
author | imarom <imarom@cisco.com> | 2015-08-18 16:21:53 +0300 |
---|---|---|
committer | imarom <imarom@cisco.com> | 2015-08-18 16:21:53 +0300 |
commit | ace4f5990ef18bb3a76d042b60d7a8af02bcd357 (patch) | |
tree | ca96a14f4f7307f6a1eee20806dded398de87abd /src | |
parent | 65b3e045334ce93162fadc85ea241b8b7667482d (diff) |
added status panel
Diffstat (limited to 'src')
-rwxr-xr-x | src/console/trex_console.py | 5 | ||||
-rw-r--r-- | src/console/trex_rpc_client.py | 10 | ||||
-rwxr-xr-x | src/console/trex_status.py | 290 | ||||
-rw-r--r-- | src/rpc-server/include/trex_rpc_server_api.h | 5 | ||||
-rw-r--r-- | src/rpc-server/src/commands/trex_rpc_cmd_general.cpp | 3 | ||||
-rw-r--r-- | src/rpc-server/src/trex_rpc_server.cpp | 14 |
6 files changed, 324 insertions, 3 deletions
diff --git a/src/console/trex_console.py b/src/console/trex_console.py index 3e452bf5..ca4c7e86 100755 --- a/src/console/trex_console.py +++ b/src/console/trex_console.py @@ -4,6 +4,7 @@ import cmd import json from trex_rpc_client import RpcClient +import trex_status class TrexConsole(cmd.Cmd): """Trex Console""" @@ -78,6 +79,10 @@ class TrexConsole(cmd.Cmd): 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''' + trex_status.show_trex_status(self.rpc_client) + def do_quit(self, line): '''\nexit the client\n''' return True diff --git a/src/console/trex_rpc_client.py b/src/console/trex_rpc_client.py index ac4aa298..04dd7727 100644 --- a/src/console/trex_rpc_client.py +++ b/src/console/trex_rpc_client.py @@ -24,7 +24,13 @@ class RpcClient(): if self.verbose:
print "\nSending Request To Server: " + str(msg) + "\n"
- self.socket.send(msg)
+ if block:
+ self.socket.send(msg)
+ else:
+ try:
+ self.socket.send(msg, flags = zmq.NOBLOCK)
+ except zmq.error.ZMQError:
+ return False, "Failed To Get Server Response"
got_response = False
@@ -67,6 +73,8 @@ class RpcClient(): return self.invoke_rpc_method("rpc_ping", block = False)
+ def get_rpc_server_status (self):
+ return self.invoke_rpc_method("rpc_get_status")
def query_rpc_server (self):
return self.invoke_rpc_method("rpc_get_reg_cmds")
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 + diff --git a/src/rpc-server/include/trex_rpc_server_api.h b/src/rpc-server/include/trex_rpc_server_api.h index 5f567a5c..bb455be2 100644 --- a/src/rpc-server/include/trex_rpc_server_api.h +++ b/src/rpc-server/include/trex_rpc_server_api.h @@ -119,8 +119,13 @@ public: void start(); void stop(); + static const std::string &get_server_uptime() { + return s_server_uptime; + } + private: std::vector<TrexRpcServerInterface *> m_servers; + static const std::string s_server_uptime; }; #endif /* __TREX_RPC_SERVER_API_H__ */ diff --git a/src/rpc-server/src/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/src/commands/trex_rpc_cmd_general.cpp index 2be4a76f..581f3a02 100644 --- a/src/rpc-server/src/commands/trex_rpc_cmd_general.cpp +++ b/src/rpc-server/src/commands/trex_rpc_cmd_general.cpp @@ -20,6 +20,7 @@ limitations under the License. */ #include "trex_rpc_cmds.h" #include <../linux_dpdk/version.h> +#include <trex_rpc_server_api.h> using namespace std; @@ -41,7 +42,7 @@ TrexRpcCmdGetStatus::_run(const Json::Value ¶ms, Json::Value &result) { section["general"]["build_date"] = get_build_date(); section["general"]["build_time"] = get_build_time(); section["general"]["version_user"] = VERSION_USER; - + section["general"]["uptime"] = TrexRpcServer::get_server_uptime(); return (RPC_CMD_OK); } diff --git a/src/rpc-server/src/trex_rpc_server.cpp b/src/rpc-server/src/trex_rpc_server.cpp index 34e60c96..17eeec6d 100644 --- a/src/rpc-server/src/trex_rpc_server.cpp +++ b/src/rpc-server/src/trex_rpc_server.cpp @@ -25,7 +25,6 @@ limitations under the License. #include <zmq.h> #include <sstream> - /************** RPC server interface ***************/ TrexRpcServerInterface::TrexRpcServerInterface(const TrexRpcServerConfig &cfg) : m_cfg(cfg) { @@ -70,6 +69,19 @@ bool TrexRpcServerInterface::is_running() { /************** RPC server *************/ +static const std::string +get_current_date_time() { + time_t now = time(0); + struct tm tstruct; + char buf[80]; + tstruct = *localtime(&now); + strftime(buf, sizeof(buf), "%d-%b-%Y / %X", &tstruct); + + return buf; +} + +const std::string TrexRpcServer::s_server_uptime = get_current_date_time(); + TrexRpcServer::TrexRpcServer(const TrexRpcServerConfig &req_resp_cfg) { /* add the request response server */ |