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) --- 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 +++++++++++++++-- 4 files changed, 80 insertions(+), 26 deletions(-) (limited to 'src/stateless') 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(-) (limited to 'src/stateless') 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 (limited to 'src/stateless') 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