From 2cacf95c9b15915f826bc421c3d32e08d3e57f64 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 3 Sep 2015 05:13:48 +0300 Subject: added RPATH support - this allows the mock server to be run from scripts --- linux/ws_main.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/linux/ws_main.py b/linux/ws_main.py index 218cbe24..dc22be07 100755 --- a/linux/ws_main.py +++ b/linux/ws_main.py @@ -249,7 +249,7 @@ PLATFORM_32 = "32" class build_option: - def __init__(self, name, src, platform, debug_mode, is_pie, use = [], flags = []): + def __init__(self, name, src, platform, debug_mode, is_pie, use = [], flags = [], rpath = []): self.mode = debug_mode; ##debug,release self.platform = platform; #['32','64'] self.is_pie = is_pie @@ -257,6 +257,7 @@ class build_option: self.src = src self.use = use self.flags = flags + self.rpath = rpath def __str__(self): s=self.mode+","+self.platform; @@ -337,6 +338,9 @@ class build_option: def get_src (self): return self.src.file_list(top) + def get_rpath (self): + return self.rpath + def get_link_flags(self): # add here basic flags base_flags = ['-pthread']; @@ -363,7 +367,8 @@ build_types = [ build_option(name = "bp-sim", src = bp, debug_mode= RELEASE_,platform = PLATFORM_32, is_pie = False), build_option(name = "bp-sim", src = bp, debug_mode= RELEASE_,platform = PLATFORM_64, is_pie = False), - build_option(name = "mock-rpc-server", use = ['zmq'], src = rpc_server_mock, debug_mode= DEBUG_,platform = PLATFORM_64, is_pie = False, flags = ['-DTREX_RPC_MOCK_SERVER']), + build_option(name = "mock-rpc-server", use = ['zmq'], src = rpc_server_mock, debug_mode= DEBUG_,platform = PLATFORM_64, is_pie = False, flags = ['-DTREX_RPC_MOCK_SERVER'], + rpath = ['.']), ] @@ -378,7 +383,7 @@ def build_prog (bld, build_obj): linkflags = build_obj.get_link_flags(), source = build_obj.get_src(), use = build_obj.get_use_libs(), - rpath = bld.env.RPATH, + rpath = bld.env.RPATH + build_obj.get_rpath(), target = build_obj.get_target()) -- cgit 1.2.3-korg From 6669a4dd3176e21ef8e99fc4a90ecd7a42566be9 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 3 Sep 2015 05:15:54 +0300 Subject: changed Is field name to isg --- src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index d1dffc44..3ddf07fa 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -48,7 +48,7 @@ TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { stream->m_self_start = parse_bool(section, "self_start", result); /* inter stream gap */ - stream->m_isg_usec = parse_double(section, "Is", result); + stream->m_isg_usec = parse_double(section, "isg", result); stream->m_next_stream_id = parse_int(section, "next_stream_id", result); -- cgit 1.2.3-korg From ea30ab5d1165707ffb1297dabf2b24ecbd5d39a5 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 3 Sep 2015 05:49:10 +0300 Subject: added pretty printer for the mock server on C++ --- src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp | 25 +++++++++++++++++++++++++ src/rpc-server/trex_rpc_jsonrpc_v2_parser.h | 8 ++++++++ src/rpc-server/trex_rpc_req_resp_server.cpp | 5 +++-- src/rpc-server/trex_rpc_server.cpp | 5 +++++ src/rpc-server/trex_rpc_server_api.h | 7 +++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp index 3831bb37..928baca6 100644 --- a/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp +++ b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp @@ -200,3 +200,28 @@ void TrexJsonRpcV2Parser::parse_single_request(Json::Value &request, commands.push_back(new JsonRpcMethod(msg_id, rpc_cmd, request["params"])); } +/** + * tries to pretty a JSON str + * + * @author imarom (03-Sep-15) + * + * @param json_str + * + * @return std::string + */ +std::string TrexJsonRpcV2Parser::pretty_json_str(const std::string &json_str) { + Json::Reader reader; + Json::Value value; + + /* basic JSON parsing */ + bool rc = reader.parse(json_str, value, false); + if (!rc) { + /* duplicate the soruce */ + return json_str; + } + + /* successfully parsed */ + Json::StyledWriter writer; + return writer.write(value); +} + diff --git a/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h index 3367ad6a..ebffaeb7 100644 --- a/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h +++ b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h @@ -79,6 +79,14 @@ public: */ void parse(std::vector &commands); + /** + * *tries* to generate a pretty string from JSON + * if json_str is not a valid JSON string + * it will duplicate the source + * + */ + static std::string pretty_json_str(const std::string &json_str); + private: /** diff --git a/src/rpc-server/trex_rpc_req_resp_server.cpp b/src/rpc-server/trex_rpc_req_resp_server.cpp index 7484758d..c4d9dfdb 100644 --- a/src/rpc-server/trex_rpc_req_resp_server.cpp +++ b/src/rpc-server/trex_rpc_req_resp_server.cpp @@ -85,7 +85,7 @@ void TrexRpcServerReqRes::_rpc_thread_cb() { /* transform it to a string */ std::string request((const char *)m_msg_buffer, msg_size); - verbose_msg("Server Received: " + request); + verbose_json("Server Received: ", TrexJsonRpcV2Parser::pretty_json_str(request)); handle_request(request); } @@ -110,6 +110,7 @@ void TrexRpcServerReqRes::_stop_rpc_thread() { */ void TrexRpcServerReqRes::handle_request(const std::string &request) { std::vector commands; + Json::FastWriter writer; Json::Value response; @@ -139,7 +140,7 @@ void TrexRpcServerReqRes::handle_request(const std::string &request) { response_str = writer.write(response); } - verbose_msg("Server Replied: " + response_str); + verbose_json("Server Replied: ", response_str); zmq_send(m_socket, response_str.c_str(), response_str.size(), 0); diff --git a/src/rpc-server/trex_rpc_server.cpp b/src/rpc-server/trex_rpc_server.cpp index 366bfc5b..6b8c200d 100644 --- a/src/rpc-server/trex_rpc_server.cpp +++ b/src/rpc-server/trex_rpc_server.cpp @@ -21,6 +21,7 @@ limitations under the License. #include #include +#include #include #include #include @@ -47,6 +48,10 @@ void TrexRpcServerInterface::verbose_msg(const std::string &msg) { std::cout << "[verbose][" << m_name << "] " << msg << "\n"; } +void TrexRpcServerInterface::verbose_json(const std::string &msg, const std::string &json_str) { + verbose_msg(msg + "\n\n" + TrexJsonRpcV2Parser::pretty_json_str(json_str)); +} + /** * starts a RPC specific server * diff --git a/src/rpc-server/trex_rpc_server_api.h b/src/rpc-server/trex_rpc_server_api.h index 6bb81c73..ab1bc454 100644 --- a/src/rpc-server/trex_rpc_server_api.h +++ b/src/rpc-server/trex_rpc_server_api.h @@ -115,6 +115,13 @@ protected: */ void verbose_msg(const std::string &msg); + /** + * prints a verbose message with a JSON to be converted to + * string + * + */ + void verbose_json(const std::string &msg, const std::string &json_str); + TrexRpcServerConfig m_cfg; bool m_is_running; bool m_is_verbose; -- cgit 1.2.3-korg From c625b6e4f6d71176ef1db5aab93f4e66939d7a25 Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 6 Sep 2015 15:23:40 +0300 Subject: trex stateless changed to singleton. added some more commands (remove all, get list of streams) --- .../client_utils/jsonrpc_client.py | 6 +- src/rpc-server/commands/trex_rpc_cmd_general.cpp | 12 +++- src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 74 +++++++++++++++++----- src/rpc-server/commands/trex_rpc_cmds.h | 2 + src/rpc-server/trex_rpc_cmds_table.cpp | 1 + src/rpc-server/trex_rpc_server.cpp | 1 + src/rpc-server/trex_rpc_server_api.h | 24 +++++++ src/rpc-server/trex_rpc_server_mock.cpp | 5 ++ src/stateless/trex_stateless.cpp | 32 ++++++---- src/stateless/trex_stateless_api.h | 40 +++++++++--- src/stateless/trex_stream.cpp | 16 +++-- src/stateless/trex_stream_api.h | 18 +++++- 12 files changed, 182 insertions(+), 49 deletions(-) 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 054dc1a2..b44b1268 100644 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -44,12 +44,12 @@ class JsonRpcClient(object): try: # int numbers - pretty_str = re.sub(r'([ ]*:[ ]*)(\-?[1-9][0-9]*[^.])',r'\1{0}\2{1}'.format(bcolors.BLUE, bcolors.ENDC), pretty_str) + pretty_str = re.sub(r'([ ]*:[ ]+)(\-?[1-9][0-9]*[^.])',r'\1{0}\2{1}'.format(bcolors.BLUE, bcolors.ENDC), pretty_str) # float - pretty_str = re.sub(r'([ ]*:[ ]*)(\-?[1-9][0-9]*\.[0-9]+)',r'\1{0}\2{1}'.format(bcolors.MAGENTA, bcolors.ENDC), pretty_str) + 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'\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 : pass diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp index 6b765aca..32952b1a 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp @@ -20,6 +20,7 @@ limitations under the License. */ #include "trex_rpc_cmds.h" #include +#include #ifndef TREX_RPC_MOCK_SERVER #include <../linux_dpdk/version.h> @@ -41,8 +42,7 @@ TrexRpcCmdGetStatus::_run(const Json::Value ¶ms, Json::Value &result) { section["general"]["version"] = VERSION_BUILD_NUM; 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(); + section["general"]["built_by"] = VERSION_USER; #else @@ -50,10 +50,16 @@ TrexRpcCmdGetStatus::_run(const Json::Value ¶ms, Json::Value &result) { section["general"]["build_date"] = __DATE__; section["general"]["build_time"] = __TIME__; section["general"]["version_user"] = "MOCK"; - section["general"]["uptime"] = TrexRpcServer::get_server_uptime(); #endif + section["general"]["uptime"] = TrexRpcServer::get_server_uptime(); + section["general"]["owner"] = TrexRpcServer::get_owner(); + + // ports + + section["ports"]["count"] = TrexStateless::get_instance().get_port_count(); + return (TREX_RPC_CMD_OK); } diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index 3ddf07fa..6dfebe6d 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -52,21 +52,25 @@ TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { stream->m_next_stream_id = parse_int(section, "next_stream_id", result); - const Json::Value &pkt = parse_array(section, "packet", result); + const Json::Value &pkt = parse_object(section, "packet", result); + const Json::Value &pkt_binary = parse_array(pkt, "binary", result); /* fetch the packet from the message */ - stream->m_pkt_len = pkt.size(); - stream->m_pkt = new uint8_t[pkt.size()]; - if (!stream->m_pkt) { + stream->m_pkt.len = pkt_binary.size(); + stream->m_pkt.binary = new uint8_t[pkt_binary.size()]; + if (!stream->m_pkt.binary) { generate_internal_err(result, "unable to allocate memory"); } /* parse the packet */ - for (int i = 0; i < pkt.size(); i++) { - stream->m_pkt[i] = parse_byte(pkt, i, result); + for (int i = 0; i < pkt_binary.size(); i++) { + stream->m_pkt.binary[i] = parse_byte(pkt_binary, i, result); } + /* meta data */ + stream->m_pkt.meta = parse_string(pkt, "meta", result); + /* parse RX info */ const Json::Value &rx = parse_object(section, "rx_stats", result); @@ -82,7 +86,7 @@ TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { /* make sure this is a valid stream to add */ validate_stream(stream, result); - TrexStatelessPort *port = get_trex_stateless()->get_port_by_id(stream->m_port_id); + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(stream->m_port_id); port->get_stream_table()->add_stream(stream); result["result"] = "ACK"; @@ -142,7 +146,7 @@ void TrexRpcCmdAddStream::validate_stream(const TrexStream *stream, Json::Value &result) { /* check packet size */ - if ( (stream->m_pkt_len < TrexStream::MIN_PKT_SIZE_BYTES) || (stream->m_pkt_len > TrexStream::MAX_PKT_SIZE_BYTES) ) { + if ( (stream->m_pkt.len < TrexStream::MIN_PKT_SIZE_BYTES) || (stream->m_pkt.len > TrexStream::MAX_PKT_SIZE_BYTES) ) { std::stringstream ss; ss << "bad packet size provided: should be between " << TrexStream::MIN_PKT_SIZE_BYTES << " and " << TrexStream::MAX_PKT_SIZE_BYTES; delete stream; @@ -150,15 +154,15 @@ TrexRpcCmdAddStream::validate_stream(const TrexStream *stream, Json::Value &resu } /* port id should be between 0 and count - 1 */ - if (stream->m_port_id >= get_trex_stateless()->get_port_count()) { + if (stream->m_port_id >= TrexStateless::get_instance().get_port_count()) { std::stringstream ss; - ss << "invalid port id - should be between 0 and " << (int)get_trex_stateless()->get_port_count() - 1; + ss << "invalid port id - should be between 0 and " << (int)TrexStateless::get_instance().get_port_count() - 1; delete stream; generate_execute_err(result, ss.str()); } /* add the stream to the port's stream table */ - TrexStatelessPort * port = get_trex_stateless()->get_port_by_id(stream->m_port_id); + TrexStatelessPort * port = TrexStateless::get_instance().get_port_by_id(stream->m_port_id); /* does such a stream exists ? */ if (port->get_stream_table()->get_stream_by_id(stream->m_stream_id)) { @@ -180,13 +184,13 @@ TrexRpcCmdRemoveStream::_run(const Json::Value ¶ms, Json::Value &result) { uint32_t stream_id = parse_int(params, "stream_id", result); - if (port_id >= get_trex_stateless()->get_port_count()) { + if (port_id >= TrexStateless::get_instance().get_port_count()) { std::stringstream ss; - ss << "invalid port id - should be between 0 and " << (int)get_trex_stateless()->get_port_count() - 1; + ss << "invalid port id - should be between 0 and " << (int)TrexStateless::get_instance().get_port_count() - 1; generate_execute_err(result, ss.str()); } - TrexStatelessPort *port = get_trex_stateless()->get_port_by_id(port_id); + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); TrexStream *stream = port->get_stream_table()->get_stream_by_id(stream_id); if (!stream) { @@ -198,6 +202,8 @@ TrexRpcCmdRemoveStream::_run(const Json::Value ¶ms, Json::Value &result) { port->get_stream_table()->remove_stream(stream); result["result"] = "ACK"; + + return (TREX_RPC_CMD_OK); } /*************************** @@ -209,15 +215,49 @@ trex_rpc_cmd_rc_e TrexRpcCmdRemoveAllStreams::_run(const Json::Value ¶ms, Json::Value &result) { uint8_t port_id = parse_byte(params, "port_id", result); - if (port_id >= get_trex_stateless()->get_port_count()) { + if (port_id >= TrexStateless::get_instance().get_port_count()) { std::stringstream ss; - ss << "invalid port id - should be between 0 and " << (int)get_trex_stateless()->get_port_count() - 1; + ss << "invalid port id - should be between 0 and " << (int)TrexStateless::get_instance().get_port_count() - 1; generate_execute_err(result, ss.str()); } - TrexStatelessPort *port = get_trex_stateless()->get_port_by_id(port_id); + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); port->get_stream_table()->remove_and_delete_all_streams(); result["result"] = "ACK"; + + return (TREX_RPC_CMD_OK); +} + +/*************************** + * get all streams configured + * on specific port + * + **************************/ +trex_rpc_cmd_rc_e +TrexRpcCmdGetStreamList::_run(const Json::Value ¶ms, Json::Value &result) { + std::vector stream_list; + + uint8_t port_id = parse_byte(params, "port_id", result); + + if (port_id >= TrexStateless::get_instance().get_port_count()) { + std::stringstream ss; + ss << "invalid port id - should be between 0 and " << (int)TrexStateless::get_instance().get_port_count() - 1; + generate_execute_err(result, ss.str()); + } + + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); + + port->get_stream_table()->get_stream_list(stream_list); + + Json::Value json_list = Json::arrayValue; + + for (auto stream_id : stream_list) { + json_list.append(stream_id); + } + + result["result"] = json_list; + + return (TREX_RPC_CMD_OK); } diff --git a/src/rpc-server/commands/trex_rpc_cmds.h b/src/rpc-server/commands/trex_rpc_cmds.h index 64551fac..e9666f21 100644 --- a/src/rpc-server/commands/trex_rpc_cmds.h +++ b/src/rpc-server/commands/trex_rpc_cmds.h @@ -74,4 +74,6 @@ void validate_stream(const TrexStream *stream, Json::Value &result); ); +TREX_RPC_CMD_DEFINE(TrexRpcCmdGetStreamList, "get_stream_list", 1); + #endif /* __TREX_RPC_CMD_H__ */ diff --git a/src/rpc-server/trex_rpc_cmds_table.cpp b/src/rpc-server/trex_rpc_cmds_table.cpp index 7d5d49ae..fc6d7b87 100644 --- a/src/rpc-server/trex_rpc_cmds_table.cpp +++ b/src/rpc-server/trex_rpc_cmds_table.cpp @@ -38,6 +38,7 @@ TrexRpcCommandsTable::TrexRpcCommandsTable() { register_command(new TrexRpcCmdAddStream()); register_command(new TrexRpcCmdRemoveStream()); register_command(new TrexRpcCmdRemoveAllStreams()); + register_command(new TrexRpcCmdGetStreamList()); } TrexRpcCommandsTable::~TrexRpcCommandsTable() { diff --git a/src/rpc-server/trex_rpc_server.cpp b/src/rpc-server/trex_rpc_server.cpp index 6b8c200d..149bb668 100644 --- a/src/rpc-server/trex_rpc_server.cpp +++ b/src/rpc-server/trex_rpc_server.cpp @@ -111,6 +111,7 @@ get_current_date_time() { } const std::string TrexRpcServer::s_server_uptime = get_current_date_time(); +std::string TrexRpcServer::s_owner = "none"; TrexRpcServer::TrexRpcServer(const TrexRpcServerConfig &req_resp_cfg) { diff --git a/src/rpc-server/trex_rpc_server_api.h b/src/rpc-server/trex_rpc_server_api.h index ab1bc454..b4313670 100644 --- a/src/rpc-server/trex_rpc_server_api.h +++ b/src/rpc-server/trex_rpc_server_api.h @@ -163,10 +163,34 @@ public: return s_server_uptime; } + + /** + * query for ownership + * + */ + static const std::string &get_owner() { + return s_owner; + } + + /** + * take ownership of the server array + * this is static + * ownership is total + * + */ + static void set_owner(const std::string &owner) { + s_owner = owner; + } + + static void clear_owner() { + s_owner = "none"; + } + private: std::vector m_servers; bool m_verbose; static const std::string s_server_uptime; + static std::string s_owner; }; #endif /* __TREX_RPC_SERVER_API_H__ */ diff --git a/src/rpc-server/trex_rpc_server_mock.cpp b/src/rpc-server/trex_rpc_server_mock.cpp index 98d1f35d..a248558e 100644 --- a/src/rpc-server/trex_rpc_server_mock.cpp +++ b/src/rpc-server/trex_rpc_server_mock.cpp @@ -20,6 +20,8 @@ limitations under the License. */ #include +#include + #include #include @@ -54,6 +56,9 @@ int main(int argc, char *argv[]) { cout << "\n-= Starting RPC Server Mock =-\n\n"; cout << "Listening on tcp://localhost:5050 [ZMQ]\n\n"; + /* configure the stateless object with 4 ports */ + TrexStateless::configure(4); + TrexRpcServerConfig rpc_cfg(TrexRpcServerConfig::RPC_PROT_TCP, 5050); TrexRpcServer rpc(rpc_cfg); diff --git a/src/stateless/trex_stateless.cpp b/src/stateless/trex_stateless.cpp index 05931983..ff469d7e 100644 --- a/src/stateless/trex_stateless.cpp +++ b/src/stateless/trex_stateless.cpp @@ -24,13 +24,30 @@ limitations under the License. * Trex stateless object * **********************************************************/ -TrexStateless::TrexStateless(uint8_t port_count) : m_port_count(port_count) { +TrexStateless::TrexStateless() { + m_is_configured = false; +} - m_ports = new TrexStatelessPort*[port_count]; +/** + * one time configuration of the stateless object + * + */ +void TrexStateless::configure(uint8_t port_count) { - for (int i = 0; i < m_port_count; i++) { - m_ports[i] = new TrexStatelessPort(i); + TrexStateless& instance = get_instance_internal(); + + if (instance.m_is_configured) { + throw TrexException("re-configuration of stateless object is not allowed"); } + + instance.m_port_count = port_count; + instance.m_ports = new TrexStatelessPort*[port_count]; + + for (int i = 0; i < instance.m_port_count; i++) { + instance.m_ports[i] = new TrexStatelessPort(i); + } + + instance.m_is_configured = true; } TrexStateless::~TrexStateless() { @@ -54,10 +71,3 @@ uint8_t TrexStateless::get_port_count() { return m_port_count; } -/******** HACK - REMOVE ME ***********/ -TrexStateless * get_trex_stateless() { - static TrexStateless trex_stateless(8); - return &trex_stateless; - -} - diff --git a/src/stateless/trex_stateless_api.h b/src/stateless/trex_stateless_api.h index 6406a946..edd7b051 100644 --- a/src/stateless/trex_stateless_api.h +++ b/src/stateless/trex_stateless_api.h @@ -71,26 +71,48 @@ private: */ class TrexStateless { public: + /** - * create a T-Rex stateless object - * - * @author imarom (31-Aug-15) + * configure the stateless object singelton + * reconfiguration is not allowed + * an exception will be thrown + */ + static void configure(uint8_t port_count); + + /** + * singleton public get instance * - * @param port_count */ - TrexStateless(uint8_t port_count); - ~TrexStateless(); + static TrexStateless& get_instance() { + TrexStateless& instance = get_instance_internal(); + + if (!instance.m_is_configured) { + throw TrexException("object is not configured"); + } + + return instance; + } TrexStatelessPort *get_port_by_id(uint8_t port_id); uint8_t get_port_count(); protected: + TrexStateless(); + ~TrexStateless(); + + static TrexStateless& get_instance_internal () { + static TrexStateless instance; + return instance; + } + + /* c++ 2011 style singleton */ + TrexStateless(TrexStateless const&) = delete; + void operator=(TrexStateless const&) = delete; + + bool m_is_configured; TrexStatelessPort **m_ports; uint8_t m_port_count; }; -/****** HACK *******/ -TrexStateless *get_trex_stateless(); - #endif /* __TREX_STATELESS_API_H__ */ diff --git a/src/stateless/trex_stream.cpp b/src/stateless/trex_stream.cpp index 1465b1ba..b3919770 100644 --- a/src/stateless/trex_stream.cpp +++ b/src/stateless/trex_stream.cpp @@ -32,16 +32,16 @@ TrexStream::TrexStream(uint8_t port_id, uint32_t stream_id) : m_port_id(port_id) m_enabled = false; m_self_start = false; - m_pkt = NULL; - m_pkt_len = 0; + m_pkt.binary = NULL; + m_pkt.len = 0; m_rx_check.m_enable = false; } TrexStream::~TrexStream() { - if (m_pkt) { - delete [] m_pkt; + if (m_pkt.binary) { + delete [] m_pkt.binary; } } @@ -91,3 +91,11 @@ TrexStream * TrexStreamTable::get_stream_by_id(uint32_t stream_id) { return NULL; } } + +void TrexStreamTable::get_stream_list(std::vector &stream_list) { + stream_list.clear(); + + for (auto stream : m_stream_table) { + stream_list.push_back(stream.first); + } +} diff --git a/src/stateless/trex_stream_api.h b/src/stateless/trex_stream_api.h index f57b7aae..97e0b7f7 100644 --- a/src/stateless/trex_stream_api.h +++ b/src/stateless/trex_stream_api.h @@ -22,7 +22,9 @@ limitations under the License. #define __TREX_STREAM_API_H__ #include +#include #include +#include class TrexRpcCmdAddStream; @@ -58,8 +60,11 @@ private: bool m_self_start; /* pkt */ - uint8_t *m_pkt; - uint16_t m_pkt_len; + struct { + uint8_t *binary; + uint16_t len; + std::string meta; + } m_pkt; /* VM */ @@ -157,6 +162,15 @@ public: */ TrexStream * get_stream_by_id(uint32_t stream_id); + /** + * populate a list with all the stream IDs + * + * @author imarom (06-Sep-15) + * + * @param stream_list + */ + void get_stream_list(std::vector &stream_list); + private: /** * holds all the stream in a hash table by stream id -- cgit 1.2.3-korg From 3adfe9c3c8a6e2ce1cdc5bd1a673e428c18fa64b Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 6 Sep 2015 17:55:00 +0300 Subject: added more commands to RPC server --- src/gtest/rpc_test.cpp | 80 +++++++++++++-- src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 128 ++++++++++++++++++++++-- src/rpc-server/commands/trex_rpc_cmds.h | 9 +- src/rpc-server/trex_rpc_cmds_table.cpp | 3 + src/rpc-server/trex_rpc_server_mock.cpp | 6 +- src/stateless/trex_stateless.cpp | 46 ++++++++- src/stateless/trex_stateless_api.h | 22 +++- src/stateless/trex_stream.cpp | 4 + src/stateless/trex_stream_api.h | 14 ++- 9 files changed, 288 insertions(+), 24 deletions(-) diff --git a/src/gtest/rpc_test.cpp b/src/gtest/rpc_test.cpp index a3df2a67..5d3c4738 100644 --- a/src/gtest/rpc_test.cpp +++ b/src/gtest/rpc_test.cpp @@ -230,13 +230,81 @@ TEST_F(RpcTest, add_stream) { Json::Value response; Json::Reader reader; - string req_str; string resp_str; - req_str = "{'stream':{'port_id':7,'stream_id':12,'enable':True,'start':True,'Is':10.0,'packet':[0,1,2,3,4]," - "'vm_data':[{'Name':'ip_cnt','Size':4,'big_edian':True,'type':'inc','core_mask':'split','init_val':'10.0.0.7','min':'10.0.0.1','max':'10.0.0.10',}]," - "'vm_program':[{'op_core':['read_to_reg_mem','write_reg_offet','write_rand_offset'],'read_name':'nameofopecodetoread','pkt_offset':20}]," - "'mode':{'type':'continues','pps':1000},'next_stream':17,'next_stream_loop':100,'rx_stats':{'enable':True,'rx_stream_id':71,'seq_enable':True,'latency':True}}}"; + // check the stream does not exists + string lookup_str = "{\"jsonrpc\":\"2.0\", \"id\":1, \"method\":\"get_stream\", \"params\":{\"port_id\":1, \"stream_id\":5}}"; + resp_str = send_msg(lookup_str); + + EXPECT_TRUE(reader.parse(resp_str, response, false)); + EXPECT_EQ(response["jsonrpc"], "2.0"); + EXPECT_EQ(response["id"], 1); + + EXPECT_EQ(response["error"]["code"], -32000); + + // add it + + string add_str = "{\"jsonrpc\":\"2.0\", \"id\":1, \"method\":\"add_stream\", \"params\":" + "{\"port_id\":1, \"stream_id\":5, \"stream\":{" + "\"mode\": {\"type\":\"continuous\", \"pps\":3}," + "\"isg\":4.3, \"enabled\":true, \"self_start\":true," + "\"next_stream_id\":-1," + "\"packet\":{\"binary\":[4,1,255], \"meta\":\"dummy\"}," + "\"rx_stats\":{\"enabled\":false}}}}"; + + resp_str = send_msg(add_str); + + EXPECT_TRUE(reader.parse(resp_str, response, false)); + EXPECT_EQ(response["jsonrpc"], "2.0"); + EXPECT_EQ(response["id"], 1); + + EXPECT_EQ(response["result"], "ACK"); + + resp_str = send_msg(lookup_str); + + EXPECT_TRUE(reader.parse(resp_str, response, false)); + EXPECT_EQ(response["jsonrpc"], "2.0"); + EXPECT_EQ(response["id"], 1); + + const Json::Value &stream = response["result"]["stream"]; + + EXPECT_EQ(stream["enabled"], true); + EXPECT_EQ(stream["self_start"], true); + + EXPECT_EQ(stream["packet"]["binary"][0], 4); + EXPECT_EQ(stream["packet"]["binary"][1], 1); + EXPECT_EQ(stream["packet"]["binary"][2], 255); + + EXPECT_EQ(stream["packet"]["meta"], "dummy"); + EXPECT_EQ(stream["next_stream_id"], -1); + + double delta = stream["isg"].asDouble() - 4.3; + EXPECT_TRUE(delta < 0.0001); + + EXPECT_EQ(stream["mode"]["type"], "continuous"); + EXPECT_EQ(stream["mode"]["pps"], 3); + + // remove it + + string remove_str = "{\"jsonrpc\":\"2.0\", \"id\":1, \"method\":\"remove_stream\", \"params\":{\"port_id\":1, \"stream_id\":5}}"; + resp_str = send_msg(remove_str); + + EXPECT_TRUE(reader.parse(resp_str, response, false)); + EXPECT_EQ(response["jsonrpc"], "2.0"); + EXPECT_EQ(response["id"], 1); + + EXPECT_EQ(response["result"], "ACK"); + + resp_str = send_msg(remove_str); + + // should not be present anymore + + EXPECT_TRUE(reader.parse(resp_str, response, false)); + EXPECT_EQ(response["jsonrpc"], "2.0"); + EXPECT_EQ(response["id"], 1); + + EXPECT_EQ(response["error"]["code"], -32000); - resp_str = send_msg(req_str); } + + diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index 6dfebe6d..16889413 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -34,6 +34,9 @@ using namespace std; trex_rpc_cmd_rc_e TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { + uint8_t port_id = parse_int(params, "port_id", result); + uint32_t stream_id = parse_int(params, "stream_id", result); + const Json::Value §ion = parse_object(params, "stream", result); /* get the type of the stream */ @@ -41,7 +44,7 @@ TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { string type = parse_string(mode, "type", result); /* allocate a new stream based on the type */ - TrexStream *stream = allocate_new_stream(section, result); + TrexStream *stream = allocate_new_stream(section, port_id, stream_id, result); /* some fields */ stream->m_enabled = parse_bool(section, "enabled", result); @@ -97,10 +100,7 @@ TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { TrexStream * -TrexRpcCmdAddStream::allocate_new_stream(const Json::Value §ion, Json::Value &result) { - - uint8_t port_id = parse_int(section, "port_id", result); - uint32_t stream_id = parse_int(section, "stream_id", result); +TrexRpcCmdAddStream::allocate_new_stream(const Json::Value §ion, uint8_t port_id, uint32_t stream_id, Json::Value &result) { TrexStream *stream; @@ -200,6 +200,7 @@ TrexRpcCmdRemoveStream::_run(const Json::Value ¶ms, Json::Value &result) { } port->get_stream_table()->remove_stream(stream); + delete stream; result["result"] = "ACK"; @@ -231,7 +232,7 @@ TrexRpcCmdRemoveAllStreams::_run(const Json::Value ¶ms, Json::Value &result) /*************************** * get all streams configured - * on specific port + * on a specific port * **************************/ trex_rpc_cmd_rc_e @@ -261,3 +262,118 @@ TrexRpcCmdGetStreamList::_run(const Json::Value ¶ms, Json::Value &result) { return (TREX_RPC_CMD_OK); } +/*************************** + * get stream by id + * on a specific port + * + **************************/ +trex_rpc_cmd_rc_e +TrexRpcCmdGetStream::_run(const Json::Value ¶ms, Json::Value &result) { + uint8_t port_id = parse_byte(params, "port_id", result); + + uint32_t stream_id = parse_int(params, "stream_id", result); + + if (port_id >= TrexStateless::get_instance().get_port_count()) { + std::stringstream ss; + ss << "invalid port id - should be between 0 and " << (int)TrexStateless::get_instance().get_port_count() - 1; + generate_execute_err(result, ss.str()); + } + + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); + + TrexStream *stream = port->get_stream_table()->get_stream_by_id(stream_id); + + if (!stream) { + std::stringstream ss; + ss << "stream id " << stream_id << " on port " << (int)port_id << " does not exists"; + generate_execute_err(result, ss.str()); + } + + Json::Value stream_json; + + stream_json["enabled"] = stream->m_enabled; + stream_json["self_start"] = stream->m_self_start; + + stream_json["isg"] = stream->m_isg_usec; + stream_json["next_stream_id"] = stream->m_next_stream_id; + + stream_json["packet"]["binary"] = Json::arrayValue; + for (int i = 0; i < stream->m_pkt.len; i++) { + stream_json["packet"]["binary"].append(stream->m_pkt.binary[i]); + } + + stream_json["packet"]["meta"] = stream->m_pkt.meta; + + if (TrexStreamContinuous *cont = dynamic_cast(stream)) { + stream_json["mode"]["type"] = "continuous"; + stream_json["mode"]["pps"] = cont->get_pps(); + + } + + result["result"]["stream"] = stream_json; + + return (TREX_RPC_CMD_OK); +} + +/*************************** + * start traffic on port + * + **************************/ +trex_rpc_cmd_rc_e +TrexRpcCmdStartTraffic::_run(const Json::Value ¶ms, Json::Value &result) { + uint8_t port_id = parse_byte(params, "port_id", result); + + if (port_id >= TrexStateless::get_instance().get_port_count()) { + std::stringstream ss; + ss << "invalid port id - should be between 0 and " << (int)TrexStateless::get_instance().get_port_count() - 1; + generate_execute_err(result, ss.str()); + } + + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); + + TrexStatelessPort::traffic_rc_e rc = port->start_traffic(); + + if (rc == TrexStatelessPort::TRAFFIC_OK) { + result["result"] = "ACK"; + } else { + std::stringstream ss; + switch (rc) { + case TrexStatelessPort::TRAFFIC_ERR_ALREADY_STARTED: + ss << "traffic has already started on that port"; + break; + case TrexStatelessPort::TRAFFIC_ERR_NO_STREAMS: + ss << "no active streams on that port"; + break; + default: + ss << "failed to start traffic"; + break; + } + + generate_execute_err(result, ss.str()); + } + + return (TREX_RPC_CMD_OK); +} + +/*************************** + * start traffic on port + * + **************************/ +trex_rpc_cmd_rc_e +TrexRpcCmdStopTraffic::_run(const Json::Value ¶ms, Json::Value &result) { + uint8_t port_id = parse_byte(params, "port_id", result); + + if (port_id >= TrexStateless::get_instance().get_port_count()) { + std::stringstream ss; + ss << "invalid port id - should be between 0 and " << (int)TrexStateless::get_instance().get_port_count() - 1; + generate_execute_err(result, ss.str()); + } + + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); + + port->stop_traffic(); + result["result"] = "ACK"; + + return (TREX_RPC_CMD_OK); +} + diff --git a/src/rpc-server/commands/trex_rpc_cmds.h b/src/rpc-server/commands/trex_rpc_cmds.h index e9666f21..428d48c1 100644 --- a/src/rpc-server/commands/trex_rpc_cmds.h +++ b/src/rpc-server/commands/trex_rpc_cmds.h @@ -65,10 +65,10 @@ TREX_RPC_CMD_DEFINE(TrexRpcCmdGetStatus, "get_status", 0); TREX_RPC_CMD_DEFINE(TrexRpcCmdRemoveAllStreams, "remove_all_streams", 1); TREX_RPC_CMD_DEFINE(TrexRpcCmdRemoveStream, "remove_stream", 2); -TREX_RPC_CMD_DEFINE_EXTENED(TrexRpcCmdAddStream, "add_stream", 1, +TREX_RPC_CMD_DEFINE_EXTENED(TrexRpcCmdAddStream, "add_stream", 3, /* extended part */ -TrexStream * allocate_new_stream(const Json::Value §ion, Json::Value &result); +TrexStream * allocate_new_stream(const Json::Value §ion, uint8_t port_id, uint32_t stream_id, Json::Value &result); void validate_stream(const TrexStream *stream, Json::Value &result); ); @@ -76,4 +76,9 @@ void validate_stream(const TrexStream *stream, Json::Value &result); TREX_RPC_CMD_DEFINE(TrexRpcCmdGetStreamList, "get_stream_list", 1); +TREX_RPC_CMD_DEFINE(TrexRpcCmdGetStream, "get_stream", 2); + +TREX_RPC_CMD_DEFINE(TrexRpcCmdStartTraffic, "start_traffic", 1); +TREX_RPC_CMD_DEFINE(TrexRpcCmdStopTraffic, "stop_traffic", 1); + #endif /* __TREX_RPC_CMD_H__ */ diff --git a/src/rpc-server/trex_rpc_cmds_table.cpp b/src/rpc-server/trex_rpc_cmds_table.cpp index fc6d7b87..71668994 100644 --- a/src/rpc-server/trex_rpc_cmds_table.cpp +++ b/src/rpc-server/trex_rpc_cmds_table.cpp @@ -39,6 +39,9 @@ TrexRpcCommandsTable::TrexRpcCommandsTable() { register_command(new TrexRpcCmdRemoveStream()); register_command(new TrexRpcCmdRemoveAllStreams()); register_command(new TrexRpcCmdGetStreamList()); + register_command(new TrexRpcCmdGetStream()); + register_command(new TrexRpcCmdStartTraffic()); + register_command(new TrexRpcCmdStopTraffic()); } TrexRpcCommandsTable::~TrexRpcCommandsTable() { diff --git a/src/rpc-server/trex_rpc_server_mock.cpp b/src/rpc-server/trex_rpc_server_mock.cpp index a248558e..835e28b8 100644 --- a/src/rpc-server/trex_rpc_server_mock.cpp +++ b/src/rpc-server/trex_rpc_server_mock.cpp @@ -44,6 +44,9 @@ int gtest_main(int argc, char **argv); int main(int argc, char *argv[]) { + /* configure the stateless object with 4 ports */ + TrexStateless::configure(4); + // gtest ? if (argc > 1) { if (string(argv[1]) != "--ut") { @@ -56,9 +59,6 @@ int main(int argc, char *argv[]) { cout << "\n-= Starting RPC Server Mock =-\n\n"; cout << "Listening on tcp://localhost:5050 [ZMQ]\n\n"; - /* configure the stateless object with 4 ports */ - TrexStateless::configure(4); - TrexRpcServerConfig rpc_cfg(TrexRpcServerConfig::RPC_PROT_TCP, 5050); TrexRpcServer rpc(rpc_cfg); diff --git a/src/stateless/trex_stateless.cpp b/src/stateless/trex_stateless.cpp index ff469d7e..2ab0c5d9 100644 --- a/src/stateless/trex_stateless.cpp +++ b/src/stateless/trex_stateless.cpp @@ -55,7 +55,7 @@ TrexStateless::~TrexStateless() { delete m_ports[i]; } - delete m_ports; + delete [] m_ports; } TrexStatelessPort * TrexStateless::get_port_by_id(uint8_t port_id) { @@ -71,3 +71,47 @@ uint8_t TrexStateless::get_port_count() { return m_port_count; } +/*************************** + * trex stateless port + * + **************************/ +TrexStatelessPort::TrexStatelessPort(uint8_t port_id) : m_port_id(port_id) { + m_started = false; +} + + +/** + * starts the traffic on the port + * + */ +TrexStatelessPort::traffic_rc_e +TrexStatelessPort::start_traffic(void) { + if (m_started) { + return (TRAFFIC_ERR_ALREADY_STARTED); + } + + if (get_stream_table()->size() == 0) { + return (TRAFFIC_ERR_NO_STREAMS); + } + + m_started = true; + + return (TRAFFIC_OK); +} + +void +TrexStatelessPort::stop_traffic(void) { + if (m_started) { + m_started = false; + } +} + +/** +* access the stream table +* +*/ +TrexStreamTable * TrexStatelessPort::get_stream_table() { + return &m_stream_table; +} + + diff --git a/src/stateless/trex_stateless_api.h b/src/stateless/trex_stateless_api.h index edd7b051..358ab339 100644 --- a/src/stateless/trex_stateless_api.h +++ b/src/stateless/trex_stateless_api.h @@ -49,20 +49,32 @@ public: class TrexStatelessPort { public: - TrexStatelessPort(uint8_t port_id) : m_port_id(port_id) { - } + /** + * describess error codes for starting traffic + */ + enum traffic_rc_e { + TRAFFIC_OK, + TRAFFIC_ERR_ALREADY_STARTED, + TRAFFIC_ERR_NO_STREAMS, + TRAFFIC_ERR_FAILED_TO_COMPILE_STREAMS + }; + + TrexStatelessPort(uint8_t port_id); + + traffic_rc_e start_traffic(void); + + void stop_traffic(void); /** * access the stream table * */ - TrexStreamTable *get_stream_table() { - return &m_stream_table; - } + TrexStreamTable *get_stream_table(); private: TrexStreamTable m_stream_table; uint8_t m_port_id; + bool m_started; }; /** diff --git a/src/stateless/trex_stream.cpp b/src/stateless/trex_stream.cpp index b3919770..2b5b2424 100644 --- a/src/stateless/trex_stream.cpp +++ b/src/stateless/trex_stream.cpp @@ -99,3 +99,7 @@ void TrexStreamTable::get_stream_list(std::vector &stream_list) { stream_list.push_back(stream.first); } } + +int TrexStreamTable::size() { + return m_stream_table.size(); +} diff --git a/src/stateless/trex_stream_api.h b/src/stateless/trex_stream_api.h index 97e0b7f7..c9248999 100644 --- a/src/stateless/trex_stream_api.h +++ b/src/stateless/trex_stream_api.h @@ -35,6 +35,7 @@ class TrexRpcCmdAddStream; class TrexStream { /* provide the RPC parser a way to access private fields */ friend class TrexRpcCmdAddStream; + friend class TrexRpcCmdGetStream; friend class TrexStreamTable; public: @@ -53,7 +54,7 @@ private: /* config fields */ double m_isg_usec; - uint32_t m_next_stream_id; + int m_next_stream_id; /* indicators */ bool m_enabled; @@ -87,6 +88,11 @@ class TrexStreamContinuous : public TrexStream { public: TrexStreamContinuous(uint8_t port_id, uint32_t stream_id, uint32_t pps) : TrexStream(port_id, stream_id), m_pps(pps) { } + + uint32_t get_pps() { + return m_pps; + } + protected: uint32_t m_pps; }; @@ -171,6 +177,12 @@ public: */ void get_stream_list(std::vector &stream_list); + /** + * get the table size + * + */ + int size(); + private: /** * holds all the stream in a hash table by stream id -- cgit 1.2.3-korg From e33befcf222fd2108d589dede11069d4256bb21a Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 7 Sep 2015 16:27:12 +0300 Subject: added VM support to the streams and RPC parser --- linux/ws_main.py | 3 +- src/gtest/rpc_test.cpp | 1 + src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 124 +++++++++++++++++ src/rpc-server/commands/trex_rpc_cmds.h | 5 +- src/rpc-server/trex_rpc_cmd.cpp | 20 +++ src/rpc-server/trex_rpc_cmd_api.h | 36 +++++ src/stateless/trex_stream_api.h | 4 + src/stateless/trex_stream_vm.cpp | 54 ++++++++ src/stateless/trex_stream_vm.h | 171 ++++++++++++++++++++++++ 9 files changed, 416 insertions(+), 2 deletions(-) create mode 100644 src/stateless/trex_stream_vm.cpp create mode 100644 src/stateless/trex_stream_vm.h diff --git a/linux/ws_main.py b/linux/ws_main.py index 5bf9a743..e9f21d11 100755 --- a/linux/ws_main.py +++ b/linux/ws_main.py @@ -141,7 +141,8 @@ net_src = SrcGroup(dir='src/common/Network/Packet', # stateless code stateless_src = SrcGroup(dir='src/stateless/', src_list=['trex_stream.cpp', - 'trex_stateless.cpp' + 'trex_stream_vm.cpp', + 'trex_stateless.cpp', ]) # RPC code rpc_server_src = SrcGroup(dir='src/rpc-server/', diff --git a/src/gtest/rpc_test.cpp b/src/gtest/rpc_test.cpp index 5d3c4738..8a7e9176 100644 --- a/src/gtest/rpc_test.cpp +++ b/src/gtest/rpc_test.cpp @@ -250,6 +250,7 @@ TEST_F(RpcTest, add_stream) { "\"isg\":4.3, \"enabled\":true, \"self_start\":true," "\"next_stream_id\":-1," "\"packet\":{\"binary\":[4,1,255], \"meta\":\"dummy\"}," + "\"vm\":[]," "\"rx_stats\":{\"enabled\":false}}}}"; resp_str = send_msg(add_str); diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index 16889413..90b55ea8 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -27,6 +27,23 @@ limitations under the License. using namespace std; +/** + * simple parser of string to number + * only difference is that it enforces whole number + * and not partial + * + */ +static uint64_t str2num(const string &str) { + size_t index; + + uint64_t num = std::stoull(str, &index, 0); + if (index != str.size()) { + throw invalid_argument("could not parse string to number"); + } + + return (num); +} + /*************************** * add new stream * @@ -74,6 +91,10 @@ TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { /* meta data */ stream->m_pkt.meta = parse_string(pkt, "meta", result); + /* parse VM */ + const Json::Value &vm = parse_array(section ,"vm", result); + parse_vm(vm, stream, result); + /* parse RX info */ const Json::Value &rx = parse_object(section, "rx_stats", result); @@ -142,6 +163,109 @@ TrexRpcCmdAddStream::allocate_new_stream(const Json::Value §ion, uint8_t por } +void +TrexRpcCmdAddStream::parse_vm_instr_checksum(const Json::Value &inst, TrexStream *stream, Json::Value &result) { + + uint16_t pkt_offset = parse_uint16(inst, "pkt_offset", result); + stream->m_vm.add_instruction(new StreamVmInstructionFixChecksumIpv4(pkt_offset)); +} + +void +TrexRpcCmdAddStream::parse_vm_instr_flow_var(const Json::Value &inst, TrexStream *stream, Json::Value &result) { + std::string flow_var_name = parse_string(inst, "name", result); + + auto sizes = {1, 2, 4, 8}; + uint8_t flow_var_size = parse_choice(inst, "size", sizes, result); + + auto ops = {"inc", "dec", "random"}; + std::string op_type_str = parse_choice(inst, "op", ops, result); + + StreamVmInstructionFlowMan::flow_var_op_e op_type; + + if (op_type_str == "inc") { + op_type = StreamVmInstructionFlowMan::FLOW_VAR_OP_INC; + } else if (op_type_str == "dec") { + op_type = StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC; + } else if (op_type_str == "random") { + op_type = StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM; + } else { + throw TrexRpcException("internal error"); + } + + std::string init_value_str = parse_string(inst, "init_value", result); + std::string min_value_str = parse_string(inst, "min_value", result); + std::string max_value_str = parse_string(inst, "max_value", result); + + uint64_t init_value; + uint64_t min_value; + uint64_t max_value; + + try { + init_value = str2num(init_value_str); + } catch (invalid_argument) { + generate_parse_err(result, "failed to parse 'init_value' as a number"); + } + + try { + min_value = str2num(min_value_str); + } catch (invalid_argument) { + generate_parse_err(result, "failed to parse 'min_value' as a number"); + } + + try { + max_value = str2num(max_value_str); + } catch (invalid_argument) { + generate_parse_err(result, "failed to parse 'max_value' as a number"); + } + + stream->m_vm.add_instruction(new StreamVmInstructionFlowMan(flow_var_name, + flow_var_size, + op_type, + init_value, + min_value, + max_value + )); +} + +void +TrexRpcCmdAddStream::parse_vm_instr_write_flow_var(const Json::Value &inst, TrexStream *stream, Json::Value &result) { + std::string flow_var_name = parse_string(inst, "flow_var_name", result); + uint16_t pkt_offset = parse_uint16(inst, "pkt_offset", result); + int add_value = parse_int(inst, "add_value", result); + bool is_big_endian = parse_bool(inst, "is_big_endian", result); + + stream->m_vm.add_instruction(new StreamVmInstructionWriteToPkt(flow_var_name, + pkt_offset, + add_value, + is_big_endian)); +} + +void +TrexRpcCmdAddStream::parse_vm(const Json::Value &vm, TrexStream *stream, Json::Value &result) { + /* array of VM instructions on vm */ + for (int i = 0; i < vm.size(); i++) { + const Json::Value & inst = vm[i]; + + auto vm_types = {"fix_checksum_ipv4", "flow_var", "write_flow_var"}; + std::string vm_type = parse_choice(inst, "type", vm_types, result); + + // checksum instruction + if (vm_type == "fix_checksum_ipv4") { + parse_vm_instr_checksum(inst, stream, result); + + } else if (vm_type == "flow_var") { + parse_vm_instr_flow_var(inst, stream, result); + + } else if (vm_type == "write_flow_var") { + parse_vm_instr_write_flow_var(inst, stream, result); + + } else { + /* internal error */ + throw TrexRpcException("internal error"); + } + } +} + void TrexRpcCmdAddStream::validate_stream(const TrexStream *stream, Json::Value &result) { diff --git a/src/rpc-server/commands/trex_rpc_cmds.h b/src/rpc-server/commands/trex_rpc_cmds.h index 428d48c1..f88631bc 100644 --- a/src/rpc-server/commands/trex_rpc_cmds.h +++ b/src/rpc-server/commands/trex_rpc_cmds.h @@ -70,7 +70,10 @@ TREX_RPC_CMD_DEFINE_EXTENED(TrexRpcCmdAddStream, "add_stream", 3, /* extended part */ TrexStream * allocate_new_stream(const Json::Value §ion, uint8_t port_id, uint32_t stream_id, Json::Value &result); void validate_stream(const TrexStream *stream, Json::Value &result); - +void parse_vm(const Json::Value &vm, TrexStream *stream, Json::Value &result); +void parse_vm_instr_checksum(const Json::Value &inst, TrexStream *stream, Json::Value &result); +void parse_vm_instr_flow_var(const Json::Value &inst, TrexStream *stream, Json::Value &result); +void parse_vm_instr_write_flow_var(const Json::Value &inst, TrexStream *stream, Json::Value &result); ); diff --git a/src/rpc-server/trex_rpc_cmd.cpp b/src/rpc-server/trex_rpc_cmd.cpp index 6988cba7..3fc77f71 100644 --- a/src/rpc-server/trex_rpc_cmd.cpp +++ b/src/rpc-server/trex_rpc_cmd.cpp @@ -50,6 +50,8 @@ TrexRpcCommand::type_to_str(field_type_e type) { switch (type) { case FIELD_TYPE_BYTE: return "byte"; + case FIELD_TYPE_UINT16: + return "uint16"; case FIELD_TYPE_BOOL: return "bool"; case FIELD_TYPE_INT: @@ -107,6 +109,18 @@ TrexRpcCommand::parse_byte(const Json::Value &parent, int index, Json::Value &re return parent[index].asUInt(); } +uint16_t +TrexRpcCommand::parse_uint16(const Json::Value &parent, const std::string &name, Json::Value &result) { + check_field_type(parent, name, FIELD_TYPE_UINT16, result); + return parent[name].asUInt(); +} + +uint16_t +TrexRpcCommand::parse_uint16(const Json::Value &parent, int index, Json::Value &result) { + check_field_type(parent, index, FIELD_TYPE_UINT16, result); + return parent[index].asUInt(); +} + int TrexRpcCommand::parse_int(const Json::Value &parent, const std::string &name, Json::Value &result) { check_field_type(parent, name, FIELD_TYPE_INT, result); @@ -190,6 +204,12 @@ TrexRpcCommand::check_field_type_common(const Json::Value &field, const std::str } break; + case FIELD_TYPE_UINT16: + if ( (!field.isUInt()) || (field.asInt() > 0xFFFF)) { + rc = false; + } + break; + case FIELD_TYPE_BOOL: if (!field.isBool()) { rc = false; diff --git a/src/rpc-server/trex_rpc_cmd_api.h b/src/rpc-server/trex_rpc_cmd_api.h index da895809..def52fca 100644 --- a/src/rpc-server/trex_rpc_cmd_api.h +++ b/src/rpc-server/trex_rpc_cmd_api.h @@ -91,6 +91,7 @@ protected: */ enum field_type_e { FIELD_TYPE_BYTE, + FIELD_TYPE_UINT16, FIELD_TYPE_INT, FIELD_TYPE_DOUBLE, FIELD_TYPE_BOOL, @@ -115,6 +116,7 @@ protected: * */ uint8_t parse_byte(const Json::Value &parent, const std::string &name, Json::Value &result); + uint16_t parse_uint16(const Json::Value &parent, const std::string &name, Json::Value &result); int parse_int(const Json::Value &parent, const std::string &name, Json::Value &result); double parse_double(const Json::Value &parent, const std::string &name, Json::Value &result); bool parse_bool(const Json::Value &parent, const std::string &name, Json::Value &result); @@ -123,6 +125,7 @@ protected: const Json::Value & parse_array(const Json::Value &parent, const std::string &name, Json::Value &result); uint8_t parse_byte(const Json::Value &parent, int index, Json::Value &result); + uint16_t parse_uint16(const Json::Value &parent, int index, Json::Value &result); int parse_int(const Json::Value &parent, int index, Json::Value &result); double parse_double(const Json::Value &parent, int index, Json::Value &result); bool parse_bool(const Json::Value &parent, int index, Json::Value &result); @@ -130,6 +133,39 @@ protected: const Json::Value & parse_object(const Json::Value &parent, int index, Json::Value &result); const Json::Value & parse_array(const Json::Value &parent, int index, Json::Value &result); + /** + * parse a field from choices + * + */ + template T parse_choice(const Json::Value ¶ms, const std::string &name, std::initializer_list choices, Json::Value &result) { + const Json::Value &field = params[name]; + + if (field == Json::Value::null) { + std::stringstream ss; + ss << "field '" << name << "' is missing"; + generate_parse_err(result, ss.str()); + } + + for (auto x : choices) { + if (field == x) { + return (x); + } + } + + std::stringstream ss; + + ss << "field '" << name << "' can only be one of ["; + for (auto x : choices) { + ss << "'" << x << "' ,"; + } + + std::string s = ss.str(); + s.pop_back(); + s.pop_back(); + s += "]"; + generate_parse_err(result, s); + } + /** * check field type * diff --git a/src/stateless/trex_stream_api.h b/src/stateless/trex_stream_api.h index c9248999..26999751 100644 --- a/src/stateless/trex_stream_api.h +++ b/src/stateless/trex_stream_api.h @@ -26,6 +26,8 @@ limitations under the License. #include #include +#include + class TrexRpcCmdAddStream; /** @@ -68,6 +70,7 @@ private: } m_pkt; /* VM */ + StreamVm m_vm; /* RX check */ struct { @@ -78,6 +81,7 @@ private: } m_rx_check; + }; /** diff --git a/src/stateless/trex_stream_vm.cpp b/src/stateless/trex_stream_vm.cpp new file mode 100644 index 00000000..2e760ae9 --- /dev/null +++ b/src/stateless/trex_stream_vm.cpp @@ -0,0 +1,54 @@ +/* + Itay Marom + Cisco Systems, Inc. +*/ + +/* +Copyright (c) 2015-2015 Cisco Systems, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#include + +/*************************** + * StreamVmInstruction + * + **************************/ +StreamVmInstruction::~StreamVmInstruction() { + +} + +/*************************** + * StreamVm + * + **************************/ +void StreamVm::add_instruction(StreamVmInstruction *inst) { + m_inst_list.push_back(inst); +} + +const std::vector & +StreamVm::get_instruction_list() { + return m_inst_list; +} + +bool StreamVm::compile() { + /* implement me */ + return (false); +} + +StreamVm::~StreamVm() { + for (auto inst : m_inst_list) { + delete inst; + } +} + diff --git a/src/stateless/trex_stream_vm.h b/src/stateless/trex_stream_vm.h new file mode 100644 index 00000000..56edbcaf --- /dev/null +++ b/src/stateless/trex_stream_vm.h @@ -0,0 +1,171 @@ +/* + Itay Marom + Cisco Systems, Inc. +*/ + +/* +Copyright (c) 2015-2015 Cisco Systems, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#ifndef __TREX_STREAM_VM_API_H__ +#define __TREX_STREAM_VM_API_H__ + +#include +#include +#include + +/** + * interface for stream VM instruction + * + */ +class StreamVmInstruction { +public: + + virtual ~StreamVmInstruction(); + +private: + static const std::string m_name; +}; + +/** + * fix checksum for ipv4 + * + */ +class StreamVmInstructionFixChecksumIpv4 : public StreamVmInstruction { +public: + StreamVmInstructionFixChecksumIpv4(uint16_t offset) : m_pkt_offset(offset) { + + } + +private: + uint16_t m_pkt_offset; +}; + +/** + * flow manipulation instruction + * + * @author imarom (07-Sep-15) + */ +class StreamVmInstructionFlowMan : public StreamVmInstruction { + +public: + + /** + * different types of operations on the object + */ + enum flow_var_op_e { + FLOW_VAR_OP_INC, + FLOW_VAR_OP_DEC, + FLOW_VAR_OP_RANDOM + }; + + StreamVmInstructionFlowMan(const std::string &var_name, + uint8_t size, + flow_var_op_e op, + uint64_t init_value, + uint64_t min_value, + uint64_t max_value) : + m_var_name(var_name), + m_size_bytes(size), + m_op(op), + m_init_value(init_value), + m_min_value(min_value), + m_max_value(max_value) { + + } + +private: + + + /* flow var name */ + std::string m_var_name; + + /* flow var size */ + uint8_t m_size_bytes; + + /* type of op */ + flow_var_op_e m_op; + + /* range */ + uint64_t m_init_value; + uint64_t m_min_value; + uint64_t m_max_value; + + +}; + +/** + * write flow var to packet + * + */ +class StreamVmInstructionWriteToPkt : public StreamVmInstruction { +public: + + StreamVmInstructionWriteToPkt(const std::string &flow_var_name, + uint16_t pkt_offset, + int32_t add_value = 0, + bool is_big_endian = true) : + + m_flow_var_name(flow_var_name), + m_pkt_offset(pkt_offset), + m_add_value(add_value), + m_is_big_endian(is_big_endian) {} +private: + + /* flow var name to write */ + std::string m_flow_var_name; + + /* where to write */ + uint16_t m_pkt_offset; + + /* add/dec value from field when writing */ + int32_t m_add_value; + + /* writing endian */ + bool m_is_big_endian; +}; + +/** + * describes a VM program + * + */ +class StreamVm { +public: + + /** + * add new instruction to the VM + * + */ + void add_instruction(StreamVmInstruction *inst); + + /** + * get const access to the instruction list + * + */ + const std::vector & get_instruction_list(); + + /** + * compile the VM + * return true of success, o.w false + * + */ + bool compile(); + + ~StreamVm(); + +private: + std::vector m_inst_list; +}; + +#endif /* __TREX_STREAM_VM_API_H__ */ -- cgit 1.2.3-korg