From 1f6977d1e109acba69f1bf2230d6b9f5e4aae54e Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 23 Nov 2015 18:02:14 +0200 Subject: add stream graph + support for console to use any of the following bps, kbps, mbps, gbps, pps, kpps, mbps percentage is not working yet --- .../trex_control_plane/console/parsing_opts.py | 64 +++++-- src/gtest/trex_stateless_gtest.cpp | 53 ++++++ src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 24 ++- src/stateless/cp/trex_stateless_port.cpp | 33 +++- src/stateless/cp/trex_stateless_port.h | 2 + src/stateless/cp/trex_stream.cpp | 2 + src/stateless/cp/trex_stream.h | 11 +- src/stateless/cp/trex_streams_compiler.cpp | 186 ++++++++++++++++++++- src/stateless/cp/trex_streams_compiler.h | 80 +++++++++ 9 files changed, 431 insertions(+), 24 deletions(-) diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index d5c21af0..732ba764 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -44,22 +44,60 @@ def match_time_unit(val): def match_multiplier(val): '''match some val against multiplier shortcut inputs ''' - match = re.match("^(\d+)(gb|kpps|%?)$", val) + + match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mbps|%?)$", val) + + result = {} + if match: - digit = int(match.group(1)) - unit = match.group(2) + + value = float(match.group(1)) + unit = match.group(3) + + # raw type (factor) if not unit: - return digit - elif unit == 'gb': - raise NotImplementedError("gb units are not supported yet") - else: - raise NotImplementedError("kpps units are not supported yet") + result['type'] = 'raw' + result['factor'] = value + + elif unit == 'bps': + result['type'] = 'max_bps' + result['max'] = value + + elif unit == 'kbps': + result['type'] = 'max_bps' + result['max'] = value * 1000 + + elif unit == 'mbps': + result['type'] = 'max_bps' + result['max'] = value * 1000 * 1000 + + elif unit == 'gbps': + result['type'] = 'max_bps' + result['max'] = value * 1000 * 1000 * 1000 + + elif unit == 'pps': + result['type'] = 'max_pps' + result['max'] = value + + elif unit == "kpps": + result['type'] = 'max_pps' + result['max'] = value * 1000 + + elif unit == "mpps": + result['type'] = 'max_pps' + result['max'] = value * 1000 * 1000 + + elif unit == "%": + raise argparse.ArgumentTypeError("percetange is currently unsupported...") + + return result + else: - raise argparse.ArgumentTypeError("Multiplier should be passed in the following format: \n" - "-m 100 : multiply stream file by this factor \n" - "-m 10gb : from graph calculate the maximum rate as this bandwidth (for each port)\n" - "-m 10kpps : from graph calculate the maximum rate as this pps (for each port)\n" - "-m 40% : from graph calculate the maximum rate as this percent from total port (for each port)") + raise argparse.ArgumentTypeError("\n\nMultiplier should be passed in the following format: \n\n" + "-m 100 : multiply by factor \n" + "-m 1bps / 1kbps / 1mbps / 1gbps : normalize to bps \n" + "-m 1pps / 1kpps / 1mbps : normalize to pps \n" + "-m 40% : normalize to % from port line rate\n") diff --git a/src/gtest/trex_stateless_gtest.cpp b/src/gtest/trex_stateless_gtest.cpp index 83721f0d..3aad2d41 100644 --- a/src/gtest/trex_stateless_gtest.cpp +++ b/src/gtest/trex_stateless_gtest.cpp @@ -1342,5 +1342,58 @@ TEST_F(basic_stl, dp_stop_event) { } +TEST_F(basic_stl, graph_generator) { + std::vector streams; + TrexStreamsGraph graph; + TrexStream *stream; + + /* stream 1 */ + stream = new TrexStream(TrexStream::stSINGLE_BURST, 0, 1); + stream->m_enabled = true; + stream->m_self_start = true; + + stream->m_isg_usec = 42; + stream->set_pps(10); + stream->set_single_burst(43281); + stream->m_pkt.len = 512; + + stream->m_next_stream_id = 2; + + + streams.push_back(stream); + + /* stream 2 */ + stream = new TrexStream(TrexStream::stMULTI_BURST, 0, 2); + stream->m_enabled = true; + stream->m_self_start = false; + + stream->set_pps(20); + stream->set_multi_burst(4918, 13, 7); + stream->m_next_stream_id = -1; + stream->m_pkt.len = 64; + + streams.push_back(stream); + + /* stream 3 */ + stream = new TrexStream(TrexStream::stCONTINUOUS, 0, 3); + stream->m_enabled = true; + stream->m_self_start = true; + + stream->m_isg_usec = 50; + stream->set_pps(30); + stream->m_next_stream_id = -1; + stream->m_pkt.len = 1512; + + streams.push_back(stream); + + + const TrexStreamsGraphObj &obj = graph.generate(streams); + EXPECT_TRUE(obj.get_max_bps() == 403840.0); + EXPECT_TRUE(obj.get_max_pps() == 50.0); + + for (auto stream : streams) { + delete stream; + } +} /********************************************* Itay Tests End *************************************/ diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index cdd13ed6..d22fda7d 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -463,7 +463,6 @@ trex_rpc_cmd_rc_e TrexRpcCmdStartTraffic::_run(const Json::Value ¶ms, Json::Value &result) { uint8_t port_id = parse_byte(params, "port_id", result); - double mul = parse_double(params, "mul", result); double duration = parse_double(params, "duration", result); if (port_id >= get_stateless_obj()->get_port_count()) { @@ -474,8 +473,29 @@ TrexRpcCmdStartTraffic::_run(const Json::Value ¶ms, Json::Value &result) { TrexStatelessPort *port = get_stateless_obj()->get_port_by_id(port_id); + + const Json::Value &mul = parse_object(params, "mul", result); + std::string mul_type = parse_string(mul, "type", result); + + /* dispatch according to type of multiplier */ + try { - port->start_traffic(mul, duration); + if (mul_type == "raw") { + + double m = parse_double(mul, "factor", result); + port->start_traffic(m, duration); + + } else if (mul_type == "max_bps") { + + double max_bps = parse_double(mul, "max", result); + port->start_traffic_max_bps(max_bps, duration); + + } else if (mul_type == "max_pps") { + + double max_pps = parse_double(mul, "max", result); + port->start_traffic_max_pps(max_pps, duration); + } + } catch (const TrexRpcException &ex) { generate_execute_err(result, ex.what()); } diff --git a/src/stateless/cp/trex_stateless_port.cpp b/src/stateless/cp/trex_stateless_port.cpp index fbc5f7c7..7dc217a3 100644 --- a/src/stateless/cp/trex_stateless_port.cpp +++ b/src/stateless/cp/trex_stateless_port.cpp @@ -111,9 +111,12 @@ TrexStatelessPort::start_traffic(double mul, double duration) { vector streams; get_object_list(streams); + /* split it per core */ + double per_core_mul = mul / m_cores_id_list.size(); + /* compiler it */ TrexStreamsCompiler compiler; - TrexStreamsCompiledObj *compiled_obj = new TrexStreamsCompiledObj(m_port_id, mul); + TrexStreamsCompiledObj *compiled_obj = new TrexStreamsCompiledObj(m_port_id, per_core_mul); bool rc = compiler.compile(streams, *compiled_obj); if (!rc) { @@ -133,6 +136,34 @@ TrexStatelessPort::start_traffic(double mul, double duration) { } +void +TrexStatelessPort::start_traffic_max_bps(double max_bps, double duration) { + /* fetch all the streams from the table */ + vector streams; + get_object_list(streams); + + TrexStreamsGraph graph; + const TrexStreamsGraphObj &obj = graph.generate(streams); + double m = (max_bps / obj.get_max_bps()); + + /* call the main function */ + start_traffic(m, duration); +} + +void +TrexStatelessPort::start_traffic_max_pps(double max_pps, double duration) { + /* fetch all the streams from the table */ + vector streams; + get_object_list(streams); + + TrexStreamsGraph graph; + const TrexStreamsGraphObj &obj = graph.generate(streams); + double m = (max_pps / obj.get_max_pps()); + + /* call the main function */ + start_traffic(m, duration); +} + /** * stop traffic on port * diff --git a/src/stateless/cp/trex_stateless_port.h b/src/stateless/cp/trex_stateless_port.h index 73157c15..9b74741c 100644 --- a/src/stateless/cp/trex_stateless_port.h +++ b/src/stateless/cp/trex_stateless_port.h @@ -77,6 +77,8 @@ public: * throws TrexException in case of an error */ void start_traffic(double mul, double duration = -1); + void start_traffic_max_bps(double max_bps, double duration = -1); + void start_traffic_max_pps(double max_pps, double duration = -1); /** * stop traffic diff --git a/src/stateless/cp/trex_stream.cpp b/src/stateless/cp/trex_stream.cpp index 5203b2a2..cad603e2 100644 --- a/src/stateless/cp/trex_stream.cpp +++ b/src/stateless/cp/trex_stream.cpp @@ -21,6 +21,7 @@ limitations under the License. #include #include #include +#include /************************************** * stream @@ -129,6 +130,7 @@ TrexStream::get_stream_json() { return m_stream_json; } + /************************************** * stream table *************************************/ diff --git a/src/stateless/cp/trex_stream.h b/src/stateless/cp/trex_stream.h index 0634829e..5baba840 100644 --- a/src/stateless/cp/trex_stream.h +++ b/src/stateless/cp/trex_stream.h @@ -90,7 +90,7 @@ public: m_next_stream_id = next_stream_id; } - double get_pps() { + double get_pps() const { return m_pps; } @@ -150,6 +150,15 @@ public: return (dp); } + + double get_burst_length_usec() const { + return ( (m_burst_total_pkts / m_pps) * 1000 ); + } + + double get_bps() const { + return (m_pps * m_pkt.len * 8); + } + void Dump(FILE *fd); public: /* basic */ diff --git a/src/stateless/cp/trex_streams_compiler.cpp b/src/stateless/cp/trex_streams_compiler.cpp index bdfc3c01..f36de1b4 100644 --- a/src/stateless/cp/trex_streams_compiler.cpp +++ b/src/stateless/cp/trex_streams_compiler.cpp @@ -34,19 +34,25 @@ limitations under the License. */ class GraphNode { public: - GraphNode(TrexStream *stream, GraphNode *next) : m_stream(stream), m_next(next) { - marked = false; + GraphNode(const TrexStream *stream, GraphNode *next) : m_stream(stream), m_next(next) { + m_marked = false; m_compressed_stream_id=-1; + } uint32_t get_stream_id() const { return m_stream->m_stream_id; } + uint32_t get_next_stream_id() const { + return m_stream->m_next_stream_id; + + } + const TrexStream *m_stream; GraphNode *m_next; std::vector m_parents; - bool marked; + bool m_marked; int m_compressed_stream_id; }; @@ -97,13 +103,13 @@ public: void clear_marks() { for (auto node : m_nodes) { - node.second->marked = false; + node.second->m_marked = false; } } void get_unmarked(std::vector &unmarked) { for (auto node : m_nodes) { - if (!node.second->marked) { + if (!node.second->m_marked) { unmarked.push_back(node.second); } } @@ -132,6 +138,7 @@ private: GraphNode m_dead_end; }; + /************************************** * stream compiled object *************************************/ @@ -316,11 +323,11 @@ TrexStreamsCompiler::check_for_unreachable_streams(GraphNodeMap *nodes) { /* pull one */ GraphNode *node = next_nodes.back(); next_nodes.pop_back(); - if (node->marked) { + if (node->m_marked) { continue; } - node->marked = true; + node->m_marked = true; if (node->m_next != NULL) { next_nodes.push_back(node->m_next); @@ -422,9 +429,174 @@ TrexStreamsCompiler::compile(const std::vector &streams, my_stream_id, my_next_stream_id ); + } return true; } +/************************************** + * streams graph + *************************************/ + +void +TrexStreamsGraph::add_rate_events_for_stream(double &offset_usec, const TrexStream *stream) { + TrexStreamsGraphObj::rate_event_st start_event; + TrexStreamsGraphObj::rate_event_st stop_event; + + switch (stream->get_type()) { + case TrexStream::stCONTINUOUS: + + start_event.time = offset_usec + stream->m_isg_usec; + start_event.diff_pps = stream->get_pps(); + start_event.diff_bps = stream->get_bps(); + m_graph_obj.add_rate_event(start_event); + + /* no more events after this stream */ + offset_usec = -1; + + break; + + case TrexStream::stSINGLE_BURST: + + /* start event */ + start_event.time = offset_usec + stream->m_isg_usec; + start_event.diff_pps = stream->get_pps(); + start_event.diff_bps = stream->get_bps(); + m_graph_obj.add_rate_event(start_event); + + /* stop event */ + stop_event.time = start_event.time + stream->get_burst_length_usec(); + stop_event.diff_pps = -(start_event.diff_pps); + stop_event.diff_bps = -(start_event.diff_bps); + m_graph_obj.add_rate_event(stop_event); + + /* next stream starts from here */ + offset_usec = stop_event.time; + + break; + + case TrexStream::stMULTI_BURST: + + double delay = stream->m_isg_usec; + + start_event.diff_pps = stream->get_pps(); + start_event.diff_bps = stream->get_bps(); + + stop_event.diff_pps = -(start_event.diff_pps); + stop_event.diff_bps = -(start_event.diff_bps); + + for (int i = 0; i < stream->m_num_bursts; i++) { + + start_event.time = offset_usec + delay; + m_graph_obj.add_rate_event(start_event); + + stop_event.time = start_event.time + stream->get_burst_length_usec(); + m_graph_obj.add_rate_event(stop_event); + + delay = stream->m_ibg_usec; + + offset_usec = stop_event.time; + } + + break; + } +} + +void +TrexStreamsGraph::generate_graph_for_one_root(uint32_t root_stream_id) { + + + std::unordered_map loop_hash; + + + uint32_t stream_id = root_stream_id; + double offset = 0; + + while (true) { + + const TrexStream *stream = m_streams_hash.at(stream_id); + /* add the node to the hash for loop detection */ + loop_hash[stream_id] = true; + + add_rate_events_for_stream(offset, stream); + + /* do we have a next stream ? */ + if (stream->m_next_stream_id == -1) { + break; + } + + /* loop detection */ + + auto search = loop_hash.find(stream->m_next_stream_id); + if (search != loop_hash.end()) { + break; + } + + /* handle the next one */ + stream_id = stream->m_next_stream_id; + } +} + +const TrexStreamsGraphObj & +TrexStreamsGraph::generate(const std::vector &streams) { + std::vector root_streams; + + + for (TrexStream *stream : streams) { + + /* for fast search we populate all the streams in a hash */ + m_streams_hash[stream->m_stream_id] = stream; + + /* hold all the self start nodes in a vector */ + if (stream->m_self_start) { + root_streams.push_back(stream->m_stream_id); + } + } + + /* for each node - scan until done or loop */ + for (uint32_t root_id : root_streams) { + generate_graph_for_one_root(root_id); + } + + + m_graph_obj.generate(); + + return m_graph_obj; +} + +/************************************** + * streams graph object + *************************************/ +void +TrexStreamsGraphObj::find_max_rate() { + double max_rate_pps = 0; + double current_rate_pps = 0; + + double max_rate_bps = 0; + double current_rate_bps = 0; + + /* now we simply walk the list and hold the max */ + for (auto &ev : m_rate_events) { + current_rate_pps += ev.diff_pps; + current_rate_bps += ev.diff_bps; + + max_rate_pps = std::max(max_rate_pps, current_rate_pps); + max_rate_bps = std::max(max_rate_bps, current_rate_bps); + } + + m_max_pps = max_rate_pps; + m_max_bps = max_rate_bps; +} + +static +bool event_compare (const TrexStreamsGraphObj::rate_event_st &first, const TrexStreamsGraphObj::rate_event_st &second) { + return (first.time < second.time); +} + +void +TrexStreamsGraphObj::generate() { + m_rate_events.sort(event_compare); + find_max_rate(); +} diff --git a/src/stateless/cp/trex_streams_compiler.h b/src/stateless/cp/trex_streams_compiler.h index 200f7ce9..28a9bd10 100644 --- a/src/stateless/cp/trex_streams_compiler.h +++ b/src/stateless/cp/trex_streams_compiler.h @@ -23,7 +23,9 @@ limitations under the License. #include #include +#include #include +#include class TrexStreamsCompiler; class TrexStream; @@ -109,6 +111,84 @@ private: void err(const std::string &err); std::vector m_warnings; + +}; + +class TrexStreamsGraph; + +/************************************** + * streams graph object + * + * holds the step graph for bandwidth + *************************************/ +class TrexStreamsGraphObj { + friend class TrexStreamsGraph; + +public: + + /** + * rate event is defined by those: + * time - the time of the event on the timeline + * diff - what is the nature of the change ? + * + * @author imarom (23-Nov-15) + */ + struct rate_event_st { + double time; + double diff_pps; + double diff_bps; + }; + + double get_max_pps() const { + return m_max_pps; + } + + double get_max_bps() const { + return m_max_bps; + } + + +private: + + void add_rate_event(const rate_event_st &ev) { + m_rate_events.push_back(ev); + } + + void generate(); + void find_max_rate(); + + double m_max_pps; + double m_max_bps; + + /* list of rate events */ + std::list m_rate_events; +}; + +/** + * graph creator + * + * @author imarom (23-Nov-15) + */ +class TrexStreamsGraph { +public: + + /** + * generate a sequence graph for streams + * + */ + const TrexStreamsGraphObj & generate(const std::vector &streams); + +private: + + void generate_graph_for_one_root(uint32_t root_stream_id); + + void add_rate_events_for_stream(double &offset, const TrexStream *stream); + + /* for fast processing of streams */ + std::unordered_map m_streams_hash; + + /* main object to hold the graph - returned to the user */ + TrexStreamsGraphObj m_graph_obj; }; #endif /* __TREX_STREAMS_COMPILER_H__ */ -- cgit 1.2.3-korg From 1e1c11059b7f7fcb5c160dffe2df832a8adf719d Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Tue, 24 Nov 2015 11:41:19 +0200 Subject: minor console fix --- scripts/automation/trex_control_plane/console/trex_console.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index c03f2a82..2a6fd2eb 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -263,12 +263,12 @@ class TRexConsole(TRexGeneralCmd): '''stops port(s) transmitting traffic\n''' self.stateless_client.cmd_stop_line(line) - ############# stop + ############# pause def do_pause(self, line): '''pause port(s) transmitting traffic\n''' self.stateless_client.cmd_pause_line(line) - ############# stop + ############# resume def do_resume(self, line): '''resume port(s) transmitting traffic\n''' self.stateless_client.cmd_resume_line(line) -- cgit 1.2.3-korg From 161a85c57c3d2a165e4fa94140d67db05714a7d3 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 24 Nov 2015 11:52:33 +0200 Subject: bug fixes for the graph --- src/gtest/trex_stateless_gtest.cpp | 128 +++++++++++++++++++++- src/stateless/cp/trex_stream.h | 5 +- src/stateless/cp/trex_streams_compiler.cpp | 170 +++++++++++++++++++++-------- src/stateless/cp/trex_streams_compiler.h | 7 ++ 4 files changed, 260 insertions(+), 50 deletions(-) diff --git a/src/gtest/trex_stateless_gtest.cpp b/src/gtest/trex_stateless_gtest.cpp index 3aad2d41..72a99e93 100644 --- a/src/gtest/trex_stateless_gtest.cpp +++ b/src/gtest/trex_stateless_gtest.cpp @@ -1342,7 +1342,7 @@ TEST_F(basic_stl, dp_stop_event) { } -TEST_F(basic_stl, graph_generator) { +TEST_F(basic_stl, graph_generator1) { std::vector streams; TrexStreamsGraph graph; TrexStream *stream; @@ -1388,12 +1388,134 @@ TEST_F(basic_stl, graph_generator) { const TrexStreamsGraphObj &obj = graph.generate(streams); - EXPECT_TRUE(obj.get_max_bps() == 403840.0); - EXPECT_TRUE(obj.get_max_pps() == 50.0); + EXPECT_EQ(obj.get_max_bps(), 405120); + EXPECT_EQ(obj.get_max_pps(), 50); for (auto stream : streams) { delete stream; } } + +TEST_F(basic_stl, graph_generator2) { + std::vector streams; + TrexStreamsGraph graph; + TrexStream *stream; + + /* add some multi burst streams */ + stream = new TrexStream(TrexStream::stMULTI_BURST, 0, 1); + stream->m_enabled = true; + stream->m_self_start = true; + + + stream->set_pps(1000); + + /* a burst of 2000 packets with a delay of 1 second */ + stream->m_isg_usec = 0; + stream->set_multi_burst(1000, 500, 1000 * 1000); + stream->m_pkt.len = 64; + + stream->m_next_stream_id = -1; + + streams.push_back(stream); + + /* another multi burst stream but with a shorter burst ( less 2 ms ) and + higher ibg (2 ms) , one milli for each side + */ + stream = new TrexStream(TrexStream::stMULTI_BURST, 0, 2); + stream->m_enabled = true; + stream->m_self_start = true; + + stream->set_pps(1000); + stream->m_isg_usec = 1000 * 1000 + 1000; + stream->set_multi_burst(1000 - 2, 1000, 1000 * 1000 + 2000); + stream->m_pkt.len = 128; + + stream->m_next_stream_id = -1; + + streams.push_back(stream); + + const TrexStreamsGraphObj &obj = graph.generate(streams); + EXPECT_EQ(obj.get_max_pps(), 1000.0); + + EXPECT_EQ(obj.get_max_bps(), (1000 * (128 + 4) * 8)); + + + for (auto stream : streams) { + delete stream; + } +} + +/* stress test */ +#if 0 +TEST_F(basic_stl, graph_generator2) { + std::vector streams; + TrexStreamsGraph graph; + TrexStream *stream; + + /* add some multi burst streams */ + stream = new TrexStream(TrexStream::stMULTI_BURST, 0, 1); + stream->m_enabled = true; + stream->m_self_start = true; + stream->m_isg_usec = 100; + + stream->set_pps(20); + stream->set_multi_burst(4918, 321312, 15); + stream->m_next_stream_id = -1; + stream->m_pkt.len = 64; + + streams.push_back(stream); + + stream = new TrexStream(TrexStream::stMULTI_BURST, 0, 2); + stream->m_enabled = true; + stream->m_self_start = true; + stream->m_isg_usec = 59281; + + stream->set_pps(30); + stream->set_multi_burst(4918, 51040, 27); + stream->m_next_stream_id = -1; + stream->m_pkt.len = 64; + + streams.push_back(stream); + + stream = new TrexStream(TrexStream::stMULTI_BURST, 0, 3); + stream->m_enabled = true; + stream->m_self_start = true; + stream->m_isg_usec = 59281492; + + stream->set_pps(40); + stream->set_multi_burst(4918, 412312, 2917); + stream->m_next_stream_id = -1; + stream->m_pkt.len = 64; + + streams.push_back(stream); + + + /* stream 3 */ + stream = new TrexStream(TrexStream::stCONTINUOUS, 0, 4); + stream->m_enabled = true; + stream->m_self_start = true; + + stream->m_isg_usec = 50; + stream->set_pps(30); + stream->m_next_stream_id = -1; + stream->m_pkt.len = 1512; + + streams.push_back(stream); + + + const TrexStreamsGraphObj &obj = graph.generate(streams); + printf("event_count is: %lu, max BPS: %f, max PPS: %f\n", obj.get_events().size(), obj.get_max_bps(), obj.get_max_pps()); + +// for (const TrexStreamsGraphObj::rate_event_st &ev : obj.get_events()) { +// printf("time: %f, diff bps: %f, diff pps: %f\n", ev.time, ev.diff_bps, ev.diff_pps); +// } + + for (auto stream : streams) { + delete stream; + } +} + +#endif + /********************************************* Itay Tests End *************************************/ diff --git a/src/stateless/cp/trex_stream.h b/src/stateless/cp/trex_stream.h index 5baba840..3e48d7e4 100644 --- a/src/stateless/cp/trex_stream.h +++ b/src/stateless/cp/trex_stream.h @@ -152,11 +152,12 @@ public: double get_burst_length_usec() const { - return ( (m_burst_total_pkts / m_pps) * 1000 ); + return ( (m_burst_total_pkts / m_pps) * 1000 * 1000); } double get_bps() const { - return (m_pps * m_pkt.len * 8); + /* packet length + 4 CRC bytes to bits and multiplied by PPS */ + return (m_pps * (m_pkt.len + 4) * 8); } void Dump(FILE *fd); diff --git a/src/stateless/cp/trex_streams_compiler.cpp b/src/stateless/cp/trex_streams_compiler.cpp index f36de1b4..f97b15b9 100644 --- a/src/stateless/cp/trex_streams_compiler.cpp +++ b/src/stateless/cp/trex_streams_compiler.cpp @@ -439,86 +439,154 @@ TrexStreamsCompiler::compile(const std::vector &streams, * streams graph *************************************/ +/** + * for each stream we create the right rate events (up/down) + * + * @author imarom (24-Nov-15) + * + * @param offset_usec + * @param stream + */ void TrexStreamsGraph::add_rate_events_for_stream(double &offset_usec, const TrexStream *stream) { - TrexStreamsGraphObj::rate_event_st start_event; - TrexStreamsGraphObj::rate_event_st stop_event; switch (stream->get_type()) { + case TrexStream::stCONTINUOUS: + add_rate_events_for_stream_cont(offset_usec, stream); + return; + + case TrexStream::stSINGLE_BURST: + add_rate_events_for_stream_single_burst(offset_usec, stream); + return; - start_event.time = offset_usec + stream->m_isg_usec; - start_event.diff_pps = stream->get_pps(); - start_event.diff_bps = stream->get_bps(); - m_graph_obj.add_rate_event(start_event); + case TrexStream::stMULTI_BURST: + add_rate_events_for_stream_multi_burst(offset_usec, stream); + return; + } +} - /* no more events after this stream */ - offset_usec = -1; +/** + * continous stream + * + */ +void +TrexStreamsGraph::add_rate_events_for_stream_cont(double &offset_usec, const TrexStream *stream) { - break; - - case TrexStream::stSINGLE_BURST: + TrexStreamsGraphObj::rate_event_st start_event; - /* start event */ - start_event.time = offset_usec + stream->m_isg_usec; - start_event.diff_pps = stream->get_pps(); - start_event.diff_bps = stream->get_bps(); - m_graph_obj.add_rate_event(start_event); + /* for debug purposes */ + start_event.stream_id = stream->m_stream_id; - /* stop event */ - stop_event.time = start_event.time + stream->get_burst_length_usec(); - stop_event.diff_pps = -(start_event.diff_pps); - stop_event.diff_bps = -(start_event.diff_bps); - m_graph_obj.add_rate_event(stop_event); + start_event.time = offset_usec + stream->m_isg_usec; + start_event.diff_pps = stream->get_pps(); + start_event.diff_bps = stream->get_bps(); + m_graph_obj.add_rate_event(start_event); - /* next stream starts from here */ - offset_usec = stop_event.time; + /* no more events after this stream */ + offset_usec = -1; +} + +/** + * single burst stream + * + */ +void +TrexStreamsGraph::add_rate_events_for_stream_single_burst(double &offset_usec, const TrexStream *stream) { + TrexStreamsGraphObj::rate_event_st start_event; + TrexStreamsGraphObj::rate_event_st stop_event; - break; - case TrexStream::stMULTI_BURST: + /* for debug purposes */ + start_event.stream_id = stream->m_stream_id; + stop_event.stream_id = stream->m_stream_id; + + /* start event */ + start_event.time = offset_usec + stream->m_isg_usec; + start_event.diff_pps = stream->get_pps(); + start_event.diff_bps = stream->get_bps(); + m_graph_obj.add_rate_event(start_event); + + /* stop event */ + stop_event.time = start_event.time + stream->get_burst_length_usec(); + stop_event.diff_pps = -(start_event.diff_pps); + stop_event.diff_bps = -(start_event.diff_bps); + m_graph_obj.add_rate_event(stop_event); + + /* next stream starts from here */ + offset_usec = stop_event.time; - double delay = stream->m_isg_usec; +} - start_event.diff_pps = stream->get_pps(); - start_event.diff_bps = stream->get_bps(); +/** + * multi burst stream + * + */ +void +TrexStreamsGraph::add_rate_events_for_stream_multi_burst(double &offset_usec, const TrexStream *stream) { + TrexStreamsGraphObj::rate_event_st start_event; + TrexStreamsGraphObj::rate_event_st stop_event; - stop_event.diff_pps = -(start_event.diff_pps); - stop_event.diff_bps = -(start_event.diff_bps); + /* first the delay is the inter stream gap */ + double delay = stream->m_isg_usec; + + /* for debug purposes */ + + start_event.diff_pps = stream->get_pps(); + start_event.diff_bps = stream->get_bps(); + start_event.stream_id = stream->m_stream_id; - for (int i = 0; i < stream->m_num_bursts; i++) { + stop_event.diff_pps = -(start_event.diff_pps); + stop_event.diff_bps = -(start_event.diff_bps); + stop_event.stream_id = stream->m_stream_id; - start_event.time = offset_usec + delay; - m_graph_obj.add_rate_event(start_event); + /* for each burst create up/down events */ + for (int i = 0; i < stream->m_num_bursts; i++) { - stop_event.time = start_event.time + stream->get_burst_length_usec(); - m_graph_obj.add_rate_event(stop_event); + start_event.time = offset_usec + delay; + m_graph_obj.add_rate_event(start_event); - delay = stream->m_ibg_usec; + stop_event.time = start_event.time + stream->get_burst_length_usec(); + m_graph_obj.add_rate_event(stop_event); - offset_usec = stop_event.time; - } + /* after the first burst, the delay is inter burst gap */ + delay = stream->m_ibg_usec; - break; + offset_usec = stop_event.time; } } +/** + * for a single root we can until done or a loop detected + * + * @author imarom (24-Nov-15) + * + * @param root_stream_id + */ void TrexStreamsGraph::generate_graph_for_one_root(uint32_t root_stream_id) { - std::unordered_map loop_hash; - + std::stringstream ss; uint32_t stream_id = root_stream_id; double offset = 0; while (true) { + const TrexStream *stream; + + /* fetch the stream from the hash - if it is not present, report an error */ + try { + stream = m_streams_hash.at(stream_id); + } catch (const std::out_of_range &e) { + ss << "stream id " << stream_id << " does not exists"; + throw TrexException(ss.str()); + } - const TrexStream *stream = m_streams_hash.at(stream_id); /* add the node to the hash for loop detection */ loop_hash[stream_id] = true; + /* create the right rate events for the stream */ add_rate_events_for_stream(offset, stream); /* do we have a next stream ? */ @@ -527,7 +595,6 @@ TrexStreamsGraph::generate_graph_for_one_root(uint32_t root_stream_id) { } /* loop detection */ - auto search = loop_hash.find(stream->m_next_stream_id); if (search != loop_hash.end()) { break; @@ -538,13 +605,25 @@ TrexStreamsGraph::generate_graph_for_one_root(uint32_t root_stream_id) { } } +/** + * for a vector of streams generate a graph of BW + * see graph object for more details + * + */ const TrexStreamsGraphObj & TrexStreamsGraph::generate(const std::vector &streams) { std::vector root_streams; - + /* before anything we create a hash streams ID + and grab the root nodes + */ for (TrexStream *stream : streams) { - + + /* skip non enabled streams */ + if (!stream->m_enabled) { + continue; + } + /* for fast search we populate all the streams in a hash */ m_streams_hash[stream->m_stream_id] = stream; @@ -578,6 +657,7 @@ TrexStreamsGraphObj::find_max_rate() { /* now we simply walk the list and hold the max */ for (auto &ev : m_rate_events) { + current_rate_pps += ev.diff_pps; current_rate_bps += ev.diff_bps; diff --git a/src/stateless/cp/trex_streams_compiler.h b/src/stateless/cp/trex_streams_compiler.h index 28a9bd10..9f0c1f8e 100644 --- a/src/stateless/cp/trex_streams_compiler.h +++ b/src/stateless/cp/trex_streams_compiler.h @@ -137,6 +137,7 @@ public: double time; double diff_pps; double diff_bps; + uint32_t stream_id; }; double get_max_pps() const { @@ -147,6 +148,9 @@ public: return m_max_bps; } + const std::list & get_events() const { + return m_rate_events; + } private: @@ -183,6 +187,9 @@ private: void generate_graph_for_one_root(uint32_t root_stream_id); void add_rate_events_for_stream(double &offset, const TrexStream *stream); + void add_rate_events_for_stream_cont(double &offset_usec, const TrexStream *stream); + void add_rate_events_for_stream_single_burst(double &offset_usec, const TrexStream *stream); + void add_rate_events_for_stream_multi_burst(double &offset_usec, const TrexStream *stream); /* for fast processing of streams */ std::unordered_map m_streams_hash; -- cgit 1.2.3-korg From dba7c1b07604f9406241b34ec0912dbf9e275b6b Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Tue, 24 Nov 2015 14:13:41 +0200 Subject: fix the pcap CRC- for statless pcap file should no include CRC --- scripts/stl/imix_1pkt.yaml | 20 +++++++------- scripts/stl/imix_2pkt.yaml | 38 +++++++++++++------------- scripts/stl/imix_3pkt.yaml | 56 +++++++++++++++++++------------------- scripts/stl/udp_1518B_no_crc.pcap | Bin 0 -> 1554 bytes scripts/stl/udp_594B_no_crc.pcap | Bin 0 -> 630 bytes scripts/stl/udp_64B_no_crc.pcap | Bin 0 -> 100 bytes 6 files changed, 57 insertions(+), 57 deletions(-) create mode 100644 scripts/stl/udp_1518B_no_crc.pcap create mode 100644 scripts/stl/udp_594B_no_crc.pcap create mode 100644 scripts/stl/udp_64B_no_crc.pcap diff --git a/scripts/stl/imix_1pkt.yaml b/scripts/stl/imix_1pkt.yaml index 511f8695..1db00685 100755 --- a/scripts/stl/imix_1pkt.yaml +++ b/scripts/stl/imix_1pkt.yaml @@ -1,11 +1,11 @@ -### Single stream UDP packet, 64B ### -##################################### -- name: udp_64B - stream: - self_start: True - packet: - binary: cap2/udp_64B.pcap - mode: - type: continuous - pps: 100 +### Single stream UDP packet, 64B ### +##################################### +- name: udp_64B + stream: + self_start: True + packet: + binary: stl/udp_64B_no_crc.pcap # pcap should not include CRC + mode: + type: continuous + pps: 100 rx_stats: [] \ No newline at end of file diff --git a/scripts/stl/imix_2pkt.yaml b/scripts/stl/imix_2pkt.yaml index 17a7bdc1..bd9089f5 100755 --- a/scripts/stl/imix_2pkt.yaml +++ b/scripts/stl/imix_2pkt.yaml @@ -1,20 +1,20 @@ -### Two-stream UDP packets, 64B and 594B ### -############################################ -- name: udp_64B - stream: - self_start: True - packet: - binary: cap2/udp_64B.pcap - mode: - type: continuous - pps: 100 - rx_stats: [] -- name: udp_594B - stream: - self_start: True - packet: - binary: cap2/udp_594B.pcap - mode: - type: continuous - pps: 100 +### Two-stream UDP packets, 64B and 594B ### +############################################ +- name: udp_64B + stream: + self_start: True + packet: + binary: stl/udp_64B_no_crc.pcap # pcap should not include CRC + mode: + type: continuous + pps: 100 + rx_stats: [] +- name: udp_594B + stream: + self_start: True + packet: + binary: stl/udp_594B_no_crc.pcap # pcap should not include CRC + mode: + type: continuous + pps: 100 rx_stats: [] \ No newline at end of file diff --git a/scripts/stl/imix_3pkt.yaml b/scripts/stl/imix_3pkt.yaml index d3923fb8..5405edb0 100755 --- a/scripts/stl/imix_3pkt.yaml +++ b/scripts/stl/imix_3pkt.yaml @@ -1,29 +1,29 @@ -### Three-stream UDP packets, 64B, 594B and 1518B ### -##################################################### -- name: udp_64B - stream: - self_start: True - packet: - binary: cap2/udp_64B.pcap - mode: - type: continuous - pps: 100 - rx_stats: [] -- name: udp_594B - stream: - self_start: True - packet: - binary: cap2/udp_594B.pcap - mode: - type: continuous - pps: 100 - rx_stats: [] -- name: udp_1518B - stream: - self_start: True - packet: - binary: cap2/udp_1518B.pcap - mode: - type: continuous - pps: 100 +### Three-stream UDP packets, 64B, 594B and 1518B ### +##################################################### +- name: udp_64B + stream: + self_start: True + packet: + binary: stl/udp_64B_no_crc.pcap # pcap should not include CRC + mode: + type: continuous + pps: 100 + rx_stats: [] +- name: udp_594B + stream: + self_start: True + packet: + binary: stl/udp_594B_no_crc.pcap # pcap should not include CRC + mode: + type: continuous + pps: 100 + rx_stats: [] +- name: udp_1518B + stream: + self_start: True + packet: + binary: stl/udp_1518B_no_crc.pcap # pcap should not include CRC + mode: + type: continuous + pps: 100 rx_stats: [] \ No newline at end of file diff --git a/scripts/stl/udp_1518B_no_crc.pcap b/scripts/stl/udp_1518B_no_crc.pcap new file mode 100644 index 00000000..145a44ad Binary files /dev/null and b/scripts/stl/udp_1518B_no_crc.pcap differ diff --git a/scripts/stl/udp_594B_no_crc.pcap b/scripts/stl/udp_594B_no_crc.pcap new file mode 100644 index 00000000..5cde2f7c Binary files /dev/null and b/scripts/stl/udp_594B_no_crc.pcap differ diff --git a/scripts/stl/udp_64B_no_crc.pcap b/scripts/stl/udp_64B_no_crc.pcap new file mode 100644 index 00000000..ab3f985b Binary files /dev/null and b/scripts/stl/udp_64B_no_crc.pcap differ -- cgit 1.2.3-korg From 7fa3f1c03c5f7b099297eead877b7a86e252f520 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Tue, 24 Nov 2015 16:04:37 +0200 Subject: v1.80 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 07a898cc..3c77d029 100755 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ -v1.79 +v1.80 -- cgit 1.2.3-korg From 045d0f8f31b2e68b8f726377fabb3205b5d19cf5 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 24 Nov 2015 18:40:16 +0200 Subject: added support for percentage on start also added info about the correct interface driver and speed from DPDK layer --- .../client/trex_stateless_client.py | 17 +++++++++-- .../trex_control_plane/console/parsing_opts.py | 18 +++++++----- src/internal_api/trex_platform_api.h | 18 ++++++++++++ src/main_dpdk.cpp | 33 ++++++++++++++++++++++ src/rpc-server/commands/trex_rpc_cmd_general.cpp | 26 +++++++++++++++-- src/stateless/cp/trex_stateless_port.cpp | 11 ++++---- src/stateless/cp/trex_stateless_port.h | 7 +++-- 7 files changed, 111 insertions(+), 19 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 7bcbf2c7..af166b7f 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -146,13 +146,14 @@ class Port(object): STATE_TX = 3 STATE_PAUSE = 4 - def __init__ (self, port_id, user, transmit): + def __init__ (self, port_id, speed, driver, user, transmit): self.port_id = port_id self.state = self.STATE_IDLE self.handler = None self.transmit = transmit self.user = user - + self.driver = driver + self.speed = speed self.streams = {} def err(self, msg): @@ -161,6 +162,9 @@ class Port(object): def ok(self): return RC_OK() + def get_speed_bps (self): + return (self.speed * 1000 * 1000) + # take the port def acquire(self, force = False): params = {"port_id": self.port_id, @@ -299,6 +303,11 @@ class Port(object): if self.state == self.STATE_TX: return self.err("Unable to start traffic - port is already transmitting") + # if percentage - translate + if mul['type'] == 'percentage': + mul['type'] = 'max_bps' + mul['max'] = self.get_speed_bps() * (mul['max'] / 100) + params = {"handler": self.handler, "port_id": self.port_id, "mul": mul, @@ -461,7 +470,9 @@ class CTRexStatelessClient(object): # create ports for port_id in xrange(self.get_port_count()): - self.ports.append(Port(port_id, self.user, self.transmit)) + speed = self.system_info['ports'][port_id]['speed'] + driver = self.system_info['ports'][port_id]['driver'] + self.ports.append(Port(port_id, speed, driver, self.user, self.transmit)) # acquire all ports rc = self.acquire() diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index 732ba764..ab678586 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -42,6 +42,12 @@ def match_time_unit(val): "-d 10m : in min \n" "-d 1h : in hours") +match_multiplier_help = """Multiplier should be passed in the following format: + [number][ | bps | kbps | mbps | gbps | pps | kpps | mpps | %% ]. + no suffix will provide an absoulute factor and percentage + will provide a percentage of the line rate. examples + : '-m 10', '-m 10kbps', '-m 10mpps', '-m 23%%' """ + def match_multiplier(val): '''match some val against multiplier shortcut inputs ''' @@ -88,16 +94,14 @@ def match_multiplier(val): result['max'] = value * 1000 * 1000 elif unit == "%": - raise argparse.ArgumentTypeError("percetange is currently unsupported...") + # will be translated by the port object + result['type'] = 'percentage' + result['max'] = value return result else: - raise argparse.ArgumentTypeError("\n\nMultiplier should be passed in the following format: \n\n" - "-m 100 : multiply by factor \n" - "-m 1bps / 1kbps / 1mbps / 1gbps : normalize to bps \n" - "-m 1pps / 1kpps / 1mbps : normalize to pps \n" - "-m 40% : normalize to % from port line rate\n") + raise argparse.ArgumentTypeError(match_multiplier_help) @@ -109,7 +113,7 @@ def is_valid_file(filename): OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], - {'help': "Set multiplier for stream", + {'help': match_multiplier_help, 'dest': "mult", 'default': 1.0, 'type': match_multiplier}), diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h index 5890a965..343b8004 100644 --- a/src/internal_api/trex_platform_api.h +++ b/src/internal_api/trex_platform_api.h @@ -24,6 +24,7 @@ limitations under the License. #include #include +#include /** * Global stats @@ -97,10 +98,20 @@ public: class TrexPlatformApi { public: + + enum driver_speed_e { + SPEED_INVALID, + SPEED_1G, + SPEED_10G, + SPEED_40G, + }; + virtual void port_id_to_cores(uint8_t port_id, std::vector> &cores_id_list) const = 0; virtual void get_global_stats(TrexPlatformGlobalStats &stats) const = 0; virtual void get_interface_stats(uint8_t interface_id, TrexPlatformInterfaceStats &stats) const = 0; + virtual void get_interface_info(uint8_t interface_id, std::string &driver_name, driver_speed_e &speed) const = 0; virtual uint8_t get_dp_core_count() const = 0; + virtual ~TrexPlatformApi() {} }; @@ -115,7 +126,9 @@ public: void port_id_to_cores(uint8_t port_id, std::vector> &cores_id_list) const; void get_global_stats(TrexPlatformGlobalStats &stats) const; void get_interface_stats(uint8_t interface_id, TrexPlatformInterfaceStats &stats) const; + void get_interface_info(uint8_t interface_id, std::string &driver_name, driver_speed_e &speed) const; uint8_t get_dp_core_count() const; + }; /** @@ -128,6 +141,11 @@ public: void port_id_to_cores(uint8_t port_id, std::vector> &cores_id_list) const {} void get_global_stats(TrexPlatformGlobalStats &stats) const; void get_interface_stats(uint8_t interface_id, TrexPlatformInterfaceStats &stats) const; + void get_interface_info(uint8_t interface_id, std::string &driver_name, driver_speed_e &speed) const { + driver_name = "MOCK"; + speed = SPEED_INVALID; + } + uint8_t get_dp_core_count() const; }; diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index b1c9ed12..57a87b71 100755 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -127,6 +127,9 @@ class CPhyEthIFStats ; class CTRexExtendedDriverBase { public: + + virtual TrexPlatformApi::driver_speed_e get_driver_speed() = 0; + virtual int get_min_sample_rate(void)=0; virtual void update_configuration(port_cfg_t * cfg)=0; virtual void update_global_config_fdir(port_cfg_t * cfg)=0; @@ -153,6 +156,10 @@ public: CTRexExtendedDriverBase1G(){ } + TrexPlatformApi::driver_speed_e get_driver_speed() { + return TrexPlatformApi::SPEED_1G; + } + static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase1G() ); } @@ -191,6 +198,10 @@ public: CGlobalInfo::m_options.preview.set_vm_one_queue_enable(true); } + TrexPlatformApi::driver_speed_e get_driver_speed() { + return TrexPlatformApi::SPEED_1G; + } + static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase1GVm() ); } @@ -229,6 +240,11 @@ class CTRexExtendedDriverBase10G : public CTRexExtendedDriverBase { public: CTRexExtendedDriverBase10G(){ } + + TrexPlatformApi::driver_speed_e get_driver_speed() { + return TrexPlatformApi::SPEED_10G; + } + static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase10G() ); } @@ -261,6 +277,10 @@ public: CTRexExtendedDriverBase40G(){ } + TrexPlatformApi::driver_speed_e get_driver_speed() { + return TrexPlatformApi::SPEED_40G; + } + static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase40G() ); } @@ -303,6 +323,11 @@ public: class CTRexExtendedDriverDb { public: + + const std::string & get_driver_name() { + return m_driver_name; + } + bool is_driver_exists(std::string name); @@ -5275,3 +5300,11 @@ TrexDpdkPlatformApi::port_id_to_cores(uint8_t port_id, std::vectorget_driver_name(); + speed = CTRexExtendedDriverDb::Ins()->get_drv()->get_driver_speed(); +} diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp index 1a7132ff..9570aae7 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp @@ -25,6 +25,8 @@ limitations under the License. #include #include +#include + #include #include #include @@ -167,14 +169,34 @@ TrexRpcCmdGetSysInfo::_run(const Json::Value ¶ms, Json::Value &result) { for (int i = 0; i < main->get_port_count(); i++) { string driver; - string speed; + TrexPlatformApi::driver_speed_e speed; TrexStatelessPort *port = main->get_port_by_id(i); port->get_properties(driver, speed); section["ports"][i]["index"] = i; + section["ports"][i]["driver"] = driver; - section["ports"][i]["speed"] = speed; + + switch (speed) { + case TrexPlatformApi::SPEED_1G: + section["ports"][i]["speed"] = 1; + break; + + case TrexPlatformApi::SPEED_10G: + section["ports"][i]["speed"] = 10; + break; + + case TrexPlatformApi::SPEED_40G: + section["ports"][i]["speed"] = 40; + break; + + default: + /* unknown value */ + section["ports"][i]["speed"] = 0; + break; + } + section["ports"][i]["owner"] = port->get_owner(); diff --git a/src/stateless/cp/trex_stateless_port.cpp b/src/stateless/cp/trex_stateless_port.cpp index be1bea12..5f1a3bca 100644 --- a/src/stateless/cp/trex_stateless_port.cpp +++ b/src/stateless/cp/trex_stateless_port.cpp @@ -56,10 +56,12 @@ TrexStatelessPort::TrexStatelessPort(uint8_t port_id, const TrexPlatformApi *api std::vector> core_pair_list; m_port_id = port_id; - m_port_state = PORT_STATE_IDLE; clear_owner(); + /* get the platform specific data */ + api->get_interface_info(port_id, m_driver_name, m_speed); + /* get the DP cores belonging to this port */ api->port_id_to_cores(m_port_id, core_pair_list); @@ -266,11 +268,10 @@ TrexStatelessPort::get_state_as_string() const { } void -TrexStatelessPort::get_properties(string &driver, string &speed) { +TrexStatelessPort::get_properties(std::string &driver, TrexPlatformApi::driver_speed_e &speed) { - /* take this from DPDK */ - driver = "e1000"; - speed = "1 Gbps"; + driver = m_driver_name; + speed = m_speed; } bool diff --git a/src/stateless/cp/trex_stateless_port.h b/src/stateless/cp/trex_stateless_port.h index 6adb5fef..20acd927 100644 --- a/src/stateless/cp/trex_stateless_port.h +++ b/src/stateless/cp/trex_stateless_port.h @@ -23,8 +23,8 @@ limitations under the License. #include #include +#include -class TrexPlatformApi; class TrexStatelessCpToDpMsgBase; /** @@ -126,7 +126,7 @@ public: * @param driver * @param speed */ - void get_properties(std::string &driver, std::string &speed); + void get_properties(std::string &driver, TrexPlatformApi::driver_speed_e &speed); /** @@ -260,6 +260,9 @@ private: port_state_e m_port_state; std::string m_owner; std::string m_owner_handler; + std::string m_driver_name; + + TrexPlatformApi::driver_speed_e m_speed; /* holds the DP cores associated with this port */ std::vector m_cores_id_list; -- cgit 1.2.3-korg From 963da09589fe8376f2750e4d0dc741d48eea6132 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 25 Nov 2015 03:51:37 -0500 Subject: added history command support - you can show the history or execute a previous command --- .../trex_control_plane/console/parsing_opts.py | 5 ++ .../trex_control_plane/console/trex_console.py | 57 +++++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index ab678586..14e8c563 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -23,6 +23,11 @@ FORCE = 10 # list of ArgumentGroup types MUTEX = 1 +def check_negative(value): + ivalue = int(value) + if ivalue < 0: + raise argparse.ArgumentTypeError("non positive value provided: '{0}'".format(value)) + return ivalue def match_time_unit(val): '''match some val against time shortcut inputs ''' diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 2a6fd2eb..0dabe2c3 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -70,6 +70,23 @@ class TRexGeneralCmd(cmd.Cmd): readline.write_history_file(self._history_file) return + def print_history (self): + + length = readline.get_current_history_length() + + for i in xrange(1, length + 1): + cmd = readline.get_history_item(i) + print "{:<5} {:}".format(i, cmd) + + def get_history_item (self, index): + length = readline.get_current_history_length() + if index > length: + print format_text("please select an index between {0} and {1}".format(0, length)) + return None + + return readline.get_history_item(index) + + def emptyline(self): """Called when an empty line is entered in response to the prompt. @@ -217,6 +234,41 @@ class TRexConsole(TRexGeneralCmd): else: print format_text("\nplease specify 'on' or 'off'\n", 'bold') + # show history + def help_history (self): + self.do_history("-h") + + def do_history (self, line): + '''Manage the command history\n''' + + item = parsing_opts.ArgumentPack(['item'], + {"nargs": '?', + 'metavar': 'item', + 'type': parsing_opts.check_negative, + 'help': "an history item index", + 'default': 0}) + + parser = parsing_opts.gen_parser(self, + "history", + self.do_history.__doc__, + item) + + opts = parser.parse_args(line.split()) + if opts is None: + return + + if opts.item == 0: + self.print_history() + else: + cmd = self.get_history_item(opts.item) + print cmd + if cmd == None: + return + + print cmd + self.onecmd(cmd) + + ############### connect def do_connect (self, line): @@ -324,7 +376,7 @@ class TRexConsole(TRexGeneralCmd): cmds = [x[3:] for x in self.get_names() if x.startswith("do_")] for cmd in cmds: - if ( (cmd == "EOF") or (cmd == "q") or (cmd == "exit")): + if ( (cmd == "EOF") or (cmd == "q") or (cmd == "exit") or (cmd == "h")): continue try: @@ -338,8 +390,9 @@ class TRexConsole(TRexGeneralCmd): print "{:<30} {:<30}".format(cmd + " - ", help) + # aliases do_exit = do_EOF = do_q = do_quit - + do_h = do_history # def is_valid_file(filename): -- cgit 1.2.3-korg From b0446de90c05d0a129fc38e6aef981dff243ccb7 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 25 Nov 2015 04:11:29 -0500 Subject: removed some garbage prints --- scripts/automation/trex_control_plane/console/trex_console.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 0dabe2c3..dc1515e0 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -261,11 +261,9 @@ class TRexConsole(TRexGeneralCmd): self.print_history() else: cmd = self.get_history_item(opts.item) - print cmd if cmd == None: return - print cmd self.onecmd(cmd) -- cgit 1.2.3-korg From 4a41c26a5cf23635a5b1332af6e806c915cf7e6c Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 25 Nov 2015 04:46:56 -0500 Subject: support for "total" flag for start also fixed a bug in mpps (trex console) --- .../trex_control_plane/client/trex_stateless_client.py | 5 +++++ .../automation/trex_control_plane/console/parsing_opts.py | 12 ++++++++++-- src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 14 +++++--------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index af166b7f..30550ca3 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -880,6 +880,7 @@ class CTRexStatelessClient(object): "start", self.cmd_start_line.__doc__, parsing_opts.PORT_LIST_WITH_ALL, + parsing_opts.TOTAL, parsing_opts.FORCE, parsing_opts.STREAM_FROM_PATH_OR_FILE, parsing_opts.DURATION, @@ -906,6 +907,10 @@ class CTRexStatelessClient(object): return RC_ERR("Failed to load stream pack") + if opts.total: + # if total was set - divide it between the ports + opts.mult['max'] = opts.mult['max'] / len(opts.ports) + return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force, opts.duration) def cmd_stop_line (self, line): diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index 14e8c563..d2484a83 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -19,6 +19,7 @@ SERVER_IP = 7 STREAM_FROM_PATH_OR_FILE = 8 DURATION = 9 FORCE = 10 +TOTAL = 11 # list of ArgumentGroup types MUTEX = 1 @@ -56,7 +57,7 @@ match_multiplier_help = """Multiplier should be passed in the following format: def match_multiplier(val): '''match some val against multiplier shortcut inputs ''' - match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mbps|%?)$", val) + match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)$", val) result = {} @@ -68,7 +69,7 @@ def match_multiplier(val): # raw type (factor) if not unit: result['type'] = 'raw' - result['factor'] = value + result['max'] = value elif unit == 'bps': result['type'] = 'max_bps' @@ -123,6 +124,13 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'default': 1.0, 'type': match_multiplier}), + + TOTAL: ArgumentPack(['-t', '--total'], + {'help': "traffic will be divided between all ports specified", + 'dest': "total", + 'default': False, + 'action': "store_true"}), + PORT_LIST: ArgumentPack(['--port'], {"nargs": '+', 'dest':'ports', diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index d22fda7d..f148261c 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -475,25 +475,21 @@ TrexRpcCmdStartTraffic::_run(const Json::Value ¶ms, Json::Value &result) { const Json::Value &mul = parse_object(params, "mul", result); + std::string mul_type = parse_string(mul, "type", result); + double max_rate = parse_double(mul, "max", result); /* dispatch according to type of multiplier */ try { if (mul_type == "raw") { - - double m = parse_double(mul, "factor", result); - port->start_traffic(m, duration); + port->start_traffic(max_rate, duration); } else if (mul_type == "max_bps") { - - double max_bps = parse_double(mul, "max", result); - port->start_traffic_max_bps(max_bps, duration); + port->start_traffic_max_bps(max_rate, duration); } else if (mul_type == "max_pps") { - - double max_pps = parse_double(mul, "max", result); - port->start_traffic_max_pps(max_pps, duration); + port->start_traffic_max_pps(max_rate, duration); } } catch (const TrexRpcException &ex) { -- cgit 1.2.3-korg From 92dea3787ab7b2c9877af9cd539d42a6957f7da6 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 25 Nov 2015 06:56:04 -0500 Subject: added events log --- .../trex_control_plane/client/trex_async_client.py | 11 +------ .../client/trex_stateless_client.py | 35 ++++++++++++++++++++++ .../trex_control_plane/console/trex_console.py | 6 ++++ src/main_dpdk.cpp | 1 + src/publisher/trex_publisher.h | 5 ++-- 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index adb91d97..7641a1e3 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -197,7 +197,6 @@ class CTRexAsyncClient(): return self.stats def get_raw_snapshot (self): - #return str(self.stats.global_stats.get('m_total_tx_bytes')) + " / " + str(self.stats.global_stats.get_rel('m_total_tx_bytes')) return self.raw_snapshot @@ -208,18 +207,10 @@ class CTRexAsyncClient(): self.stats.update(data) # events elif name == "trex-event": - self.__handle_async_event(type, data) + self.stateless_client.handle_async_event(type, data) else: - # ignore pass - def __handle_async_event (self, type, data): - # DP stopped - if (type == 0): - port_id = int(data['port_id']) - print format_text("\n[Event] - Port {0} Stopped".format(port_id), 'bold') - # call the handler - self.stateless_client.async_event_port_stopped(port_id) def stop (self): self.active = False diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 30550ca3..1d0ca606 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -16,6 +16,7 @@ from collections import namedtuple from common.text_opts import * import parsing_opts import time +import datetime from trex_async_client import CTRexAsyncClient @@ -400,10 +401,44 @@ class CTRexStatelessClient(object): self.connected = False + self.events = [] + ################# events handler ###################### + + def handle_async_event (self, type, data): + # DP stopped + + ev = "[event] - " + + if (type == 0): + port_id = int(data['port_id']) + ev += "Port {0} has stopped".format(port_id) + # call the handler + self.async_event_port_stopped(port_id) + + elif (type == 1): + ev += "Server has stopped" + self.async_event_server_stopped() + + else: + # unknown event - ignore + return + + print format_text("\n" + ev, 'bold') + + ts = time.time() + st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') + self.events.append("{0} - ".format(st) + format_text(ev, 'bold')) + def async_event_port_stopped (self, port_id): self.ports[port_id].async_event_port_stopped() + def async_event_server_stopped (self): + self.disconnect() + + def get_events (self): + return self.events + ############# helper functions section ############## def validate_port_list(self, port_id_list): diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index dc1515e0..b164af4e 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -334,6 +334,12 @@ class TRexConsole(TRexGeneralCmd): self.stateless_client.cmd_reset() + def do_events (self, line): + '''shows events recieved from server\n''' + events = self.stateless_client.get_events() + for ev in events: + print ev + # tui def do_tui (self, line): '''Shows a graphical console\n''' diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 57a87b71..3e5418b9 100755 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -3274,6 +3274,7 @@ void CGlobalTRex::try_stop_all_dp(){ delay(100); } if ( all_core_finished ){ + m_zmq_publisher.publish_event(TrexPublisher::EVENT_SERVER_STOPPED); printf(" All cores stopped !! \n"); }else{ printf(" ERROR one of the DP core is stucked !\n"); diff --git a/src/publisher/trex_publisher.h b/src/publisher/trex_publisher.h index 82603fda..89336735 100644 --- a/src/publisher/trex_publisher.h +++ b/src/publisher/trex_publisher.h @@ -39,10 +39,11 @@ public: void publish_json(const std::string &s); enum event_type_e { - EVENT_PORT_STOPPED = 0 + EVENT_PORT_STOPPED = 0, + EVENT_SERVER_STOPPED = 1 }; - void publish_event(event_type_e type, const Json::Value &data); + void publish_event(event_type_e type, const Json::Value &data = Json::nullValue); private: void show_zmq_last_error(const std::string &err); -- cgit 1.2.3-korg From b6ec2066653319b60385de1d4117165eb88890a1 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 25 Nov 2015 07:39:39 -0500 Subject: fixed a bug with default start command in the console also added clear events and more types of events --- .../client/trex_stateless_client.py | 30 ++++++++++++++++++++-- .../trex_control_plane/console/parsing_opts.py | 2 +- .../trex_control_plane/console/trex_console.py | 22 ++++++++++++++++ src/publisher/trex_publisher.h | 6 +++-- src/stateless/cp/trex_stateless_port.cpp | 9 ++++++- 5 files changed, 63 insertions(+), 6 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 1d0ca606..149d2855 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -410,26 +410,49 @@ class CTRexStatelessClient(object): ev = "[event] - " + show_event = False + + # port started if (type == 0): + port_id = int(data['port_id']) + ev += "Port {0} has started".format(port_id) + + # port stopped + elif (type == 1): port_id = int(data['port_id']) ev += "Port {0} has stopped".format(port_id) + # call the handler self.async_event_port_stopped(port_id) + - elif (type == 1): + # server stopped + elif (type == 2): ev += "Server has stopped" self.async_event_server_stopped() + show_event = True + + # port finished traffic + elif (type == 3): + port_id = int(data['port_id']) + ev += "Port {0} job done".format(port_id) + + # call the handler + self.async_event_port_stopped(port_id) + show_event = True else: # unknown event - ignore return - print format_text("\n" + ev, 'bold') + if show_event: + print format_text("\n" + ev, 'bold') ts = time.time() st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') self.events.append("{0} - ".format(st) + format_text(ev, 'bold')) + def async_event_port_stopped (self, port_id): self.ports[port_id].async_event_port_stopped() @@ -439,6 +462,9 @@ class CTRexStatelessClient(object): def get_events (self): return self.events + def clear_events (self): + self.events = [] + ############# helper functions section ############## def validate_port_list(self, port_id_list): diff --git a/scripts/automation/trex_control_plane/console/parsing_opts.py b/scripts/automation/trex_control_plane/console/parsing_opts.py index d2484a83..0bcdce84 100755 --- a/scripts/automation/trex_control_plane/console/parsing_opts.py +++ b/scripts/automation/trex_control_plane/console/parsing_opts.py @@ -121,7 +121,7 @@ def is_valid_file(filename): OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'help': match_multiplier_help, 'dest': "mult", - 'default': 1.0, + 'default': {'type':'raw', 'max':1}, 'type': match_multiplier}), diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index b164af4e..fc2c845a 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -334,12 +334,34 @@ class TRexConsole(TRexGeneralCmd): self.stateless_client.cmd_reset() + def help_events (self): + self.do_events("-h") + def do_events (self, line): '''shows events recieved from server\n''' + + x = parsing_opts.ArgumentPack(['-c','--clear'], + {'action' : "store_true", + 'default': False, + 'help': "clear the events log"}) + + parser = parsing_opts.gen_parser(self, + "events", + self.do_events.__doc__, + x) + + opts = parser.parse_args(line.split()) + if opts is None: + return + events = self.stateless_client.get_events() for ev in events: print ev + if opts.clear: + self.stateless_client.clear_events() + print format_text("\n\nEvent log was cleared\n\n") + # tui def do_tui (self, line): '''Shows a graphical console\n''' diff --git a/src/publisher/trex_publisher.h b/src/publisher/trex_publisher.h index 89336735..8d1be064 100644 --- a/src/publisher/trex_publisher.h +++ b/src/publisher/trex_publisher.h @@ -39,8 +39,10 @@ public: void publish_json(const std::string &s); enum event_type_e { - EVENT_PORT_STOPPED = 0, - EVENT_SERVER_STOPPED = 1 + EVENT_PORT_STARTED = 0, + EVENT_PORT_STOPPED = 1, + EVENT_SERVER_STOPPED = 2, + EVENT_PORT_FINISHED_TX = 3, }; void publish_event(event_type_e type, const Json::Value &data = Json::nullValue); diff --git a/src/stateless/cp/trex_stateless_port.cpp b/src/stateless/cp/trex_stateless_port.cpp index 5f1a3bca..2fd948fd 100644 --- a/src/stateless/cp/trex_stateless_port.cpp +++ b/src/stateless/cp/trex_stateless_port.cpp @@ -139,6 +139,9 @@ TrexStatelessPort::start_traffic(double mul, double duration) { send_message_to_dp(start_msg); + Json::Value data; + data["port_id"] = m_port_id; + get_stateless_obj()->get_publisher()->publish_event(TrexPublisher::EVENT_PORT_STARTED, data); } void @@ -194,6 +197,10 @@ TrexStatelessPort::stop_traffic(void) { change_state(PORT_STATE_STREAMS); + Json::Value data; + data["port_id"] = m_port_id; + get_stateless_obj()->get_publisher()->publish_event(TrexPublisher::EVENT_PORT_STOPPED, data); + } void @@ -366,7 +373,7 @@ TrexStatelessPort::on_dp_event_occured(TrexDpPortEvent::event_e event_type) { /* send a ZMQ event */ data["port_id"] = m_port_id; - get_stateless_obj()->get_publisher()->publish_event(TrexPublisher::EVENT_PORT_STOPPED, data); + get_stateless_obj()->get_publisher()->publish_event(TrexPublisher::EVENT_PORT_FINISHED_TX, data); break; default: -- cgit 1.2.3-korg From 12a19244693cacbd77ca0a8e43f382a10f773f91 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Wed, 25 Nov 2015 17:20:44 +0200 Subject: move to real idle in case of stop --- src/stateless/dp/trex_stateless_dp_core.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stateless/dp/trex_stateless_dp_core.cpp b/src/stateless/dp/trex_stateless_dp_core.cpp index 9b4a6ad9..4e429c8f 100644 --- a/src/stateless/dp/trex_stateless_dp_core.cpp +++ b/src/stateless/dp/trex_stateless_dp_core.cpp @@ -309,6 +309,7 @@ TrexStatelessDpCore::start_scheduler() { /* bail out in case of terminate */ if (m_state != TrexStatelessDpCore::STATE_TERMINATE) { m_core->m_node_gen.close_file(m_core); + m_state = STATE_IDLE; /* we exit from all ports and we have nothing to do, we move to IDLE state */ } } -- cgit 1.2.3-korg From d9a11302236095e055247295021bdfce6c988802 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 26 Nov 2015 02:30:03 -0500 Subject: added support for 'update' --- .../client/trex_stateless_client.py | 88 ++++++++++++++++++++-- .../trex_control_plane/console/trex_console.py | 16 +++- src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 49 +++++++++--- src/stateless/cp/trex_stateless_port.cpp | 27 +++---- src/stateless/cp/trex_stateless_port.h | 14 +++- src/stateless/dp/trex_stateless_dp_core.cpp | 34 +++++++-- src/stateless/dp/trex_stateless_dp_core.h | 16 +++- src/stateless/dp/trex_stream_node.h | 12 ++- .../messaging/trex_stateless_messaging.cpp | 16 ++++ src/stateless/messaging/trex_stateless_messaging.h | 20 +++++ 10 files changed, 243 insertions(+), 49 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 149d2855..c5b28f5b 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -164,7 +164,7 @@ class Port(object): return RC_OK() def get_speed_bps (self): - return (self.speed * 1000 * 1000) + return (self.speed * 1000 * 1000 * 1000) # take the port def acquire(self, force = False): @@ -293,6 +293,13 @@ class Port(object): return self.streams + def process_mul (self, mul): + # if percentage - translate + if mul['type'] == 'percentage': + mul['type'] = 'max_bps' + mul['max'] = self.get_speed_bps() * (mul['max'] / 100) + + # start traffic def start (self, mul, duration): if self.state == self.STATE_DOWN: @@ -304,11 +311,8 @@ class Port(object): if self.state == self.STATE_TX: return self.err("Unable to start traffic - port is already transmitting") - # if percentage - translate - if mul['type'] == 'percentage': - mul['type'] = 'max_bps' - mul['max'] = self.get_speed_bps() * (mul['max'] / 100) - + self.process_mul(mul) + params = {"handler": self.handler, "port_id": self.port_id, "mul": mul, @@ -358,6 +362,7 @@ class Port(object): return self.ok() + def resume (self): if (self.state != self.STATE_PAUSE) : @@ -375,6 +380,23 @@ class Port(object): return self.ok() + + def update (self, mul): + if (self.state != self.STATE_TX) : + return self.err("port is not transmitting") + + self.process_mul(mul) + + params = {"handler": self.handler, + "port_id": self.port_id, + "mul": mul} + + rc, data = self.transmit("update_traffic", params) + if not rc: + return self.err(data) + + return self.ok() + ################# events handler ###################### def async_event_port_stopped (self): self.state = self.STATE_STREAMS @@ -752,6 +774,17 @@ class CTRexStatelessClient(object): return rc + def update_traffic (self, mult, port_id_list = None, force = False): + + port_id_list = self.__ports(port_id_list) + rc = RC() + + for port_id in port_id_list: + rc.add(self.ports[port_id].update(mult)) + + return rc + + def get_port_stats(self, port_id=None): pass @@ -832,6 +865,25 @@ class CTRexStatelessClient(object): return RC_OK() + # update cmd + def cmd_update (self, port_id_list, mult): + + # find the relveant ports + active_ports = list(set(self.get_active_ports()).intersection(port_id_list)) + + if not active_ports: + msg = "No active traffic on porvided ports" + print format_text(msg, 'bold') + return RC_ERR(msg) + + rc = self.update_traffic(mult, active_ports) + rc.annotate("Updating traffic on port(s) {0}:".format(port_id_list)) + if rc.bad(): + return rc + + return RC_OK() + + # pause cmd def cmd_pause (self, port_id_list): @@ -968,7 +1020,8 @@ class CTRexStatelessClient(object): return RC_ERR("Failed to load stream pack") - if opts.total: + # total has no meaning with percentage - its linear + if opts.total and (mult['type'] != 'percentage'): # if total was set - divide it between the ports opts.mult['max'] = opts.mult['max'] / len(opts.ports) @@ -988,6 +1041,27 @@ class CTRexStatelessClient(object): return self.cmd_stop(opts.ports) + def cmd_update_line (self, line): + '''Update port(s) speed currently active\n''' + parser = parsing_opts.gen_parser(self, + "update", + self.cmd_update_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL, + parsing_opts.MULTIPLIER, + parsing_opts.TOTAL) + + opts = parser.parse_args(line.split()) + if opts is None: + return RC_ERR("bad command line paramters") + + # total has no meaning with percentage - its linear + if opts.total and (opts.mult['type'] != 'percentage'): + # if total was set - divide it between the ports + opts.mult['max'] = opts.mult['max'] / len(opts.ports) + + return self.cmd_update(opts.ports, opts.mult) + + def cmd_reset_line (self, line): return self.cmd_reset() diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index fc2c845a..9e44daac 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -313,6 +313,17 @@ class TRexConsole(TRexGeneralCmd): '''stops port(s) transmitting traffic\n''' self.stateless_client.cmd_stop_line(line) + def help_stop(self): + self.do_stop("-h") + + ############# update + def do_update(self, line): + '''update speed of port(s)currently transmitting traffic\n''' + self.stateless_client.cmd_update_line(line) + + def help_update (self): + self.do_update("-h") + ############# pause def do_pause(self, line): '''pause port(s) transmitting traffic\n''' @@ -323,10 +334,7 @@ class TRexConsole(TRexGeneralCmd): '''resume port(s) transmitting traffic\n''' self.stateless_client.cmd_resume_line(line) - - - def help_stop(self): - self.do_stop("-h") + ########## reset def do_reset (self, line): diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index f148261c..96224d4e 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -479,18 +479,26 @@ TrexRpcCmdStartTraffic::_run(const Json::Value ¶ms, Json::Value &result) { std::string mul_type = parse_string(mul, "type", result); double max_rate = parse_double(mul, "max", result); + + double m = 0; + /* dispatch according to type of multiplier */ + if (mul_type == "raw") { + m = max_rate; - try { - if (mul_type == "raw") { - port->start_traffic(max_rate, duration); + } else if (mul_type == "max_bps") { + m = port->calculate_m_from_bps(max_rate); - } else if (mul_type == "max_bps") { - port->start_traffic_max_bps(max_rate, duration); + } else if (mul_type == "max_pps") { + m = port->calculate_m_from_pps(max_rate); - } else if (mul_type == "max_pps") { - port->start_traffic_max_pps(max_rate, duration); - } + } else { + generate_parse_err(result, "multiplier type can be either 'raw', 'max_bps' or 'max_pps'"); + } + + + try { + port->start_traffic(m, duration); } catch (const TrexRpcException &ex) { generate_execute_err(result, ex.what()); @@ -633,7 +641,6 @@ trex_rpc_cmd_rc_e TrexRpcCmdUpdateTraffic::_run(const Json::Value ¶ms, Json::Value &result) { uint8_t port_id = parse_byte(params, "port_id", result); - double mul = parse_double(params, "mul", result); if (port_id >= get_stateless_obj()->get_port_count()) { std::stringstream ss; @@ -643,8 +650,30 @@ TrexRpcCmdUpdateTraffic::_run(const Json::Value ¶ms, Json::Value &result) { TrexStatelessPort *port = get_stateless_obj()->get_port_by_id(port_id); + /* multiplier */ + const Json::Value &mul = parse_object(params, "mul", result); + + std::string mul_type = parse_string(mul, "type", result); + double max_rate = parse_double(mul, "max", result); + + double m = 0; + + /* dispatch according to type of multiplier */ + if (mul_type == "raw") { + m = max_rate; + + } else if (mul_type == "max_bps") { + m = port->calculate_m_from_bps(max_rate); + + } else if (mul_type == "max_pps") { + m = port->calculate_m_from_pps(max_rate); + + } else { + generate_parse_err(result, "multiplier type can be either 'raw', 'max_bps' or 'max_pps'"); + } + try { - port->update_traffic(mul); + port->update_traffic(m); } catch (const TrexRpcException &ex) { generate_execute_err(result, ex.what()); } diff --git a/src/stateless/cp/trex_stateless_port.cpp b/src/stateless/cp/trex_stateless_port.cpp index 2fd948fd..8e18a5bf 100644 --- a/src/stateless/cp/trex_stateless_port.cpp +++ b/src/stateless/cp/trex_stateless_port.cpp @@ -144,32 +144,29 @@ TrexStatelessPort::start_traffic(double mul, double duration) { get_stateless_obj()->get_publisher()->publish_event(TrexPublisher::EVENT_PORT_STARTED, data); } -void -TrexStatelessPort::start_traffic_max_bps(double max_bps, double duration) { + +double +TrexStatelessPort::calculate_m_from_bps(double max_bps) { /* fetch all the streams from the table */ vector streams; get_object_list(streams); TrexStreamsGraph graph; const TrexStreamsGraphObj &obj = graph.generate(streams); - double m = (max_bps / obj.get_max_bps()); - /* call the main function */ - start_traffic(m, duration); + return (max_bps / obj.get_max_bps()); } -void -TrexStatelessPort::start_traffic_max_pps(double max_pps, double duration) { +double +TrexStatelessPort::calculate_m_from_pps(double max_pps) { /* fetch all the streams from the table */ vector streams; get_object_list(streams); TrexStreamsGraph graph; const TrexStreamsGraphObj &obj = graph.generate(streams); - double m = (max_pps / obj.get_max_pps()); - /* call the main function */ - start_traffic(m, duration); + return (max_pps / obj.get_max_pps()); } /** @@ -239,16 +236,14 @@ TrexStatelessPort::resume_traffic(void) { void TrexStatelessPort::update_traffic(double mul) { - verify_state(PORT_STATE_STREAMS | PORT_STATE_TX | PORT_STATE_PAUSE); + verify_state(PORT_STATE_TX | PORT_STATE_PAUSE); - #if 0 /* generate a message to all the relevant DP cores to start transmitting */ - TrexStatelessCpToDpMsgBase *stop_msg = new TrexStatelessDpStop(m_port_id); + double per_core_mul = mul / m_cores_id_list.size(); + TrexStatelessCpToDpMsgBase *update_msg = new TrexStatelessDpUpdate(m_port_id, per_core_mul); - send_message_to_dp(stop_msg); + send_message_to_dp(update_msg); - m_port_state = PORT_STATE_UP_IDLE; - #endif } std::string diff --git a/src/stateless/cp/trex_stateless_port.h b/src/stateless/cp/trex_stateless_port.h index 20acd927..b061a414 100644 --- a/src/stateless/cp/trex_stateless_port.h +++ b/src/stateless/cp/trex_stateless_port.h @@ -77,8 +77,18 @@ public: * throws TrexException in case of an error */ void start_traffic(double mul, double duration = -1); - void start_traffic_max_bps(double max_bps, double duration = -1); - void start_traffic_max_pps(double max_pps, double duration = -1); + + /** + * given a BPS rate calculate ther correct M for this port + * + */ + double calculate_m_from_bps(double max_bps); + + /** + * given a PPS rate calculate the correct M for this port + * + */ + double calculate_m_from_pps(double max_pps); /** * stop traffic diff --git a/src/stateless/dp/trex_stateless_dp_core.cpp b/src/stateless/dp/trex_stateless_dp_core.cpp index 4e429c8f..dd4937cd 100644 --- a/src/stateless/dp/trex_stateless_dp_core.cpp +++ b/src/stateless/dp/trex_stateless_dp_core.cpp @@ -139,6 +139,20 @@ bool TrexStatelessDpPerPort::resume_traffic(uint8_t port_id){ return (true); } +bool TrexStatelessDpPerPort::update_traffic(uint8_t port_id, double mul) { + + assert( (m_state == TrexStatelessDpPerPort::ppSTATE_TRANSMITTING || + (m_state == TrexStatelessDpPerPort::ppSTATE_PAUSE)) ); + + for (auto dp_stream : m_active_nodes) { + CGenNodeStateless * node = dp_stream.m_node; + assert(node->get_port_id() == port_id); + + node->set_multiplier(mul); + } + + return (true); +} bool TrexStatelessDpPerPort::pause_traffic(uint8_t port_id){ @@ -402,9 +416,9 @@ TrexStatelessDpCore::add_port_duration(double duration, void -TrexStatelessDpCore::add_cont_stream(TrexStatelessDpPerPort * lp_port, - TrexStream * stream, - TrexStreamsCompiledObj *comp) { +TrexStatelessDpCore::add_stream(TrexStatelessDpPerPort * lp_port, + TrexStream * stream, + TrexStreamsCompiledObj *comp) { CGenNodeStateless *node = m_core->create_node_sl(); @@ -439,8 +453,8 @@ TrexStatelessDpCore::add_cont_stream(TrexStatelessDpPerPort * lp_port, node->m_pause =0; node->m_stream_type = stream->m_type; - node->m_next_time_offset = 1.0 / (stream->get_pps() * comp->get_multiplier()) ; - + node->m_base_pps = stream->get_pps(); + node->set_multiplier(comp->get_multiplier()); /* stateless specific fields */ switch ( stream->m_type ) { @@ -520,7 +534,7 @@ TrexStatelessDpCore::start_traffic(TrexStreamsCompiledObj *obj, for (auto single_stream : obj->get_objects()) { /* all commands should be for the same port */ assert(obj->get_port_id() == single_stream.m_stream->m_port_id); - add_cont_stream(lp_port,single_stream.m_stream,obj); + add_stream(lp_port,single_stream.m_stream,obj); } uint32_t nodes = lp_port->m_active_nodes.size(); @@ -582,6 +596,14 @@ TrexStatelessDpCore::pause_traffic(uint8_t port_id){ lp_port->pause_traffic(port_id); } +void +TrexStatelessDpCore::update_traffic(uint8_t port_id, double mul) { + + TrexStatelessDpPerPort * lp_port = get_port_db(port_id); + + lp_port->update_traffic(port_id, mul); +} + void TrexStatelessDpCore::stop_traffic(uint8_t port_id, diff --git a/src/stateless/dp/trex_stateless_dp_core.h b/src/stateless/dp/trex_stateless_dp_core.h index eda1ae59..563159b2 100644 --- a/src/stateless/dp/trex_stateless_dp_core.h +++ b/src/stateless/dp/trex_stateless_dp_core.h @@ -68,6 +68,8 @@ public: bool resume_traffic(uint8_t port_id); + bool update_traffic(uint8_t port_id, double mul); + bool stop_traffic(uint8_t port_id, bool stop_on_id, int event_id); @@ -158,6 +160,14 @@ public: void resume_traffic(uint8_t port_id); + /** + * update current traffic rate + * + * @author imarom (25-Nov-15) + * + */ + void update_traffic(uint8_t port_id, double mul); + /** * * stop all traffic for this core @@ -250,9 +260,9 @@ private: void add_global_duration(double duration); - void add_cont_stream(TrexStatelessDpPerPort * lp_port, - TrexStream * stream, - TrexStreamsCompiledObj *comp); + void add_stream(TrexStatelessDpPerPort * lp_port, + TrexStream * stream, + TrexStreamsCompiledObj *comp); uint8_t m_thread_id; uint8_t m_local_port_offset; diff --git a/src/stateless/dp/trex_stream_node.h b/src/stateless/dp/trex_stream_node.h index ccf99eaa..5997376f 100644 --- a/src/stateless/dp/trex_stream_node.h +++ b/src/stateless/dp/trex_stream_node.h @@ -86,8 +86,9 @@ private: TrexStream * m_ref_stream_info; /* the stream info */ CGenNodeStateless * m_next_stream; + double m_base_pps; /* pad to match the size of CGenNode */ - uint8_t m_pad_end[56]; + uint8_t m_pad_end[48]; @@ -99,6 +100,15 @@ public: } + /** + * calculate the time offset based + * on the PPS and multiplier + * + */ + void set_multiplier(double mul) { + m_next_time_offset = 1.0 / (m_base_pps * mul) ; + } + /* we restart the stream, schedule it using stream isg */ inline void update_refresh_time(double cur_time){ m_time = cur_time + usec_to_sec(m_ref_stream_info->m_isg_usec); diff --git a/src/stateless/messaging/trex_stateless_messaging.cpp b/src/stateless/messaging/trex_stateless_messaging.cpp index ec8b7839..3210f29a 100644 --- a/src/stateless/messaging/trex_stateless_messaging.cpp +++ b/src/stateless/messaging/trex_stateless_messaging.cpp @@ -163,6 +163,22 @@ TrexStatelessDpCanQuit::clone(){ return new_msg; } +/************************* + update traffic message + ************************/ +bool +TrexStatelessDpUpdate::handle(TrexStatelessDpCore *dp_core) { + dp_core->update_traffic(m_port_id, m_mul); + + return true; +} + +TrexStatelessCpToDpMsgBase * +TrexStatelessDpUpdate::clone() { + TrexStatelessCpToDpMsgBase *new_msg = new TrexStatelessDpUpdate(m_port_id, m_mul); + + return new_msg; +} /************************* messages from DP to CP **********************/ bool diff --git a/src/stateless/messaging/trex_stateless_messaging.h b/src/stateless/messaging/trex_stateless_messaging.h index 6bd0dbe3..7390be60 100644 --- a/src/stateless/messaging/trex_stateless_messaging.h +++ b/src/stateless/messaging/trex_stateless_messaging.h @@ -225,6 +225,26 @@ public: }; +/** + * update message + */ +class TrexStatelessDpUpdate : public TrexStatelessCpToDpMsgBase { +public: + + TrexStatelessDpUpdate(uint8_t port_id, double mul) { + m_port_id = port_id; + m_mul = mul; + } + + virtual bool handle(TrexStatelessDpCore *dp_core); + + virtual TrexStatelessCpToDpMsgBase * clone(); + +private: + uint8_t m_port_id; + double m_mul; +}; + /************************* messages from DP to CP **********************/ -- cgit 1.2.3-korg