summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorimarom <imarom@cisco.com>2015-08-18 16:21:53 +0300
committerimarom <imarom@cisco.com>2015-08-18 16:21:53 +0300
commitace4f5990ef18bb3a76d042b60d7a8af02bcd357 (patch)
treeca96a14f4f7307f6a1eee20806dded398de87abd /src
parent65b3e045334ce93162fadc85ea241b8b7667482d (diff)
added status panel
Diffstat (limited to 'src')
-rwxr-xr-xsrc/console/trex_console.py5
-rw-r--r--src/console/trex_rpc_client.py10
-rwxr-xr-xsrc/console/trex_status.py290
-rw-r--r--src/rpc-server/include/trex_rpc_server_api.h5
-rw-r--r--src/rpc-server/src/commands/trex_rpc_cmd_general.cpp3
-rw-r--r--src/rpc-server/src/trex_rpc_server.cpp14
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 &params, 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 */