diff options
Diffstat (limited to 'src/rpc-server')
-rw-r--r-- | src/rpc-server/commands/trex_rpc_cmd_general.cpp | 267 | ||||
-rw-r--r-- | src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 488 | ||||
-rw-r--r-- | src/rpc-server/commands/trex_rpc_cmd_test.cpp | 51 | ||||
-rw-r--r-- | src/rpc-server/commands/trex_rpc_cmds.h | 104 | ||||
-rw-r--r-- | src/rpc-server/include/trex_rpc_cmd_api.h | 90 | ||||
-rw-r--r-- | src/rpc-server/src/commands/trex_rpc_cmd_general.cpp | 49 | ||||
-rw-r--r-- | src/rpc-server/src/commands/trex_rpc_cmd_test.cpp | 126 | ||||
-rw-r--r-- | src/rpc-server/src/commands/trex_rpc_cmds.h | 89 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_cmd.cpp | 317 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_cmd_api.h | 241 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_cmds_table.cpp (renamed from src/rpc-server/src/trex_rpc_cmds_table.cpp) | 20 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_cmds_table.h (renamed from src/rpc-server/include/trex_rpc_cmds_table.h) | 0 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_exception_api.h (renamed from src/rpc-server/include/trex_rpc_exception_api.h) | 3 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp (renamed from src/rpc-server/src/trex_rpc_jsonrpc_v2_parser.cpp) | 53 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_jsonrpc_v2_parser.h (renamed from src/rpc-server/include/trex_rpc_jsonrpc_v2_parser.h) | 17 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_req_resp_server.cpp (renamed from src/rpc-server/src/trex_rpc_req_resp_server.cpp) | 32 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_req_resp_server.h (renamed from src/rpc-server/include/trex_rpc_req_resp_server.h) | 4 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_server.cpp (renamed from src/rpc-server/src/trex_rpc_server.cpp) | 5 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_server_api.h (renamed from src/rpc-server/include/trex_rpc_server_api.h) | 15 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_server_mock.cpp (renamed from src/rpc-server/src/trex_rpc_server_mock.cpp) | 7 |
20 files changed, 1612 insertions, 366 deletions
diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp new file mode 100644 index 00000000..106a167a --- /dev/null +++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp @@ -0,0 +1,267 @@ +/* + 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 "trex_rpc_cmds.h" +#include <trex_rpc_server_api.h> +#include <trex_stateless_api.h> +#include <trex_rpc_cmds_table.h> + +#include <fstream> +#include <iostream> +#include <unistd.h> + +#ifndef TREX_RPC_MOCK_SERVER + #include <../linux_dpdk/version.h> +#endif + +using namespace std; + +/** + * ping command + */ +trex_rpc_cmd_rc_e +TrexRpcCmdPing::_run(const Json::Value ¶ms, Json::Value &result) { + + result["result"] = "ACK"; + return (TREX_RPC_CMD_OK); +} + +/** + * query command + */ +trex_rpc_cmd_rc_e +TrexRpcCmdGetCmds::_run(const Json::Value ¶ms, Json::Value &result) { + vector<string> cmds; + + TrexRpcCommandsTable::get_instance().query(cmds); + + Json::Value test = Json::arrayValue; + for (auto cmd : cmds) { + test.append(cmd); + } + + result["result"] = test; + + return (TREX_RPC_CMD_OK); +} + +/** + * get version + * + */ +trex_rpc_cmd_rc_e +TrexRpcCmdGetVersion::_run(const Json::Value ¶ms, Json::Value &result) { + + Json::Value §ion = result["result"]; + + #ifndef TREX_RPC_MOCK_SERVER + + section["version"] = VERSION_BUILD_NUM; + section["build_date"] = get_build_date(); + section["build_time"] = get_build_time(); + section["built_by"] = VERSION_USER; + + #else + + section["version"] = "v0.0"; + section["build_date"] = __DATE__; + section["build_time"] = __TIME__; + section["built_by"] = "MOCK"; + + #endif + + return (TREX_RPC_CMD_OK); +} + +/** + * get the CPU model + * + */ +std::string +TrexRpcCmdGetSysInfo::get_cpu_model() { + + static const string cpu_prefix = "model name"; + std::ifstream cpuinfo("/proc/cpuinfo"); + + if (cpuinfo.is_open()) { + while (cpuinfo.good()) { + + std::string line; + getline(cpuinfo, line); + + int pos = line.find(cpu_prefix); + if (pos == string::npos) { + continue; + } + + /* trim it */ + int index = cpu_prefix.size() + 1; + while ( (line[index] == ' ') || (line[index] == ':') ) { + index++; + } + + return line.substr(index); + } + } + + return "unknown"; +} + +void +TrexRpcCmdGetSysInfo::get_hostname(string &hostname) { + char buffer[256]; + buffer[0] = 0; + + gethostname(buffer, sizeof(buffer)); + + /* write hostname */ + hostname = buffer; +} + +/** + * get system info + * + */ +trex_rpc_cmd_rc_e +TrexRpcCmdGetSysInfo::_run(const Json::Value ¶ms, Json::Value &result) { + string hostname; + + TrexStateless & instance = TrexStateless::get_instance(); + + Json::Value §ion = result["result"]; + + get_hostname(hostname); + section["hostname"] = hostname; + + section["uptime"] = TrexRpcServer::get_server_uptime(); + + /* FIXME: core count */ + section["dp_core_count"] = 1; + section["core_type"] = get_cpu_model(); + + /* ports */ + + + section["port_count"] = instance.get_port_count(); + + section["ports"] = Json::arrayValue; + + for (int i = 0; i < instance.get_port_count(); i++) { + string driver; + string speed; + + TrexStatelessPort *port = instance.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; + + section["ports"][i]["owner"] = port->get_owner(); + + switch (port->get_state()) { + case TrexStatelessPort::PORT_STATE_DOWN: + section["ports"][i]["status"] = "down"; + break; + + case TrexStatelessPort::PORT_STATE_UP_IDLE: + section["ports"][i]["status"] = "idle"; + break; + + case TrexStatelessPort::PORT_STATE_TRANSMITTING: + section["ports"][i]["status"] = "transmitting"; + break; + } + + } + + return (TREX_RPC_CMD_OK); +} + +/** + * returns the current owner of the device + * + * @author imarom (08-Sep-15) + * + * @param params + * @param result + * + * @return trex_rpc_cmd_rc_e + */ +trex_rpc_cmd_rc_e +TrexRpcCmdGetOwner::_run(const Json::Value ¶ms, Json::Value &result) { + Json::Value §ion = result["result"]; + + uint8_t port_id = parse_port(params, result); + + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); + section["owner"] = port->get_owner(); + + return (TREX_RPC_CMD_OK); +} + +/** + * acquire device + * + */ +trex_rpc_cmd_rc_e +TrexRpcCmdAcquire::_run(const Json::Value ¶ms, Json::Value &result) { + + uint8_t port_id = parse_port(params, result); + + const string &new_owner = parse_string(params, "user", result); + bool force = parse_bool(params, "force", result); + + /* if not free and not you and not force - fail */ + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); + + if ( (!port->is_free_to_aquire()) && (port->get_owner() != new_owner) && (!force)) { + generate_execute_err(result, "device is already taken by '" + port->get_owner() + "'"); + } + + port->set_owner(new_owner); + + result["result"] = port->get_owner_handler(); + + return (TREX_RPC_CMD_OK); +} + +/** + * release device + * + */ +trex_rpc_cmd_rc_e +TrexRpcCmdRelease::_run(const Json::Value ¶ms, Json::Value &result) { + + uint8_t port_id = parse_port(params, result); + + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); + + if (port->get_state() == TrexStatelessPort::PORT_STATE_TRANSMITTING) { + generate_execute_err(result, "cannot release a port during transmission"); + } + + port->clear_owner(); + + result["result"] = "ACK"; + + return (TREX_RPC_CMD_OK); +} diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp new file mode 100644 index 00000000..1450e1a9 --- /dev/null +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -0,0 +1,488 @@ +/* + 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 "trex_rpc_cmds.h" +#include <trex_rpc_server_api.h> +#include <trex_stream_api.h> +#include <trex_stateless_api.h> + +#include <iostream> + +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 + * + **************************/ +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 */ + const Json::Value &mode = parse_object(section, "mode", result); + string type = parse_string(mode, "type", result); + + /* allocate a new stream based on the type */ + TrexStream *stream = allocate_new_stream(section, port_id, stream_id, result); + + /* save this for future queries */ + stream->store_stream_json(section); + + /* some fields */ + stream->m_enabled = parse_bool(section, "enabled", result); + stream->m_self_start = parse_bool(section, "self_start", result); + + /* inter stream gap */ + stream->m_isg_usec = parse_double(section, "isg", result); + + stream->m_next_stream_id = parse_int(section, "next_stream_id", result); + + const Json::Value &pkt = parse_object(section, "packet", result); + const Json::Value &pkt_binary = parse_array(pkt, "binary", result); + + /* fetch the packet from the message */ + + stream->m_pkt.len = pkt_binary.size(); + stream->m_pkt.binary = new uint8_t[pkt_binary.size()]; + if (!stream->m_pkt.binary) { + generate_internal_err(result, "unable to allocate memory"); + } + + /* parse the packet */ + for (int i = 0; i < pkt_binary.size(); i++) { + stream->m_pkt.binary[i] = parse_byte(pkt_binary, i, result); + } + + /* meta data */ + stream->m_pkt.meta = parse_string(pkt, "meta", result); + + /* parse 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); + + stream->m_rx_check.m_enable = parse_bool(rx, "enabled", result); + + /* if it is enabled - we need more fields */ + if (stream->m_rx_check.m_enable) { + stream->m_rx_check.m_stream_id = parse_int(rx, "stream_id", result); + stream->m_rx_check.m_seq_enabled = parse_bool(rx, "seq_enabled", result); + stream->m_rx_check.m_latency = parse_bool(rx, "latency", result); + } + + /* make sure this is a valid stream to add */ + validate_stream(stream, result); + + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(stream->m_port_id); + port->get_stream_table()->add_stream(stream); + + result["result"] = "ACK"; + + return (TREX_RPC_CMD_OK); +} + + + +TrexStream * +TrexRpcCmdAddStream::allocate_new_stream(const Json::Value §ion, uint8_t port_id, uint32_t stream_id, Json::Value &result) { + + TrexStream *stream; + + const Json::Value &mode = parse_object(section, "mode", result); + std::string type = parse_string(mode, "type", result); + + if (type == "continuous") { + + double pps = parse_double(mode, "pps", result); + stream = new TrexStreamContinuous(port_id, stream_id, pps); + + } else if (type == "single_burst") { + + uint32_t total_pkts = parse_int(mode, "total_pkts", result); + double pps = parse_double(mode, "pps", result); + + stream = new TrexStreamBurst(port_id, stream_id, total_pkts, pps); + + } else if (type == "multi_burst") { + + double pps = parse_double(mode, "pps", result); + double ibg_usec = parse_double(mode, "ibg", result); + uint32_t num_bursts = parse_int(mode, "number_of_bursts", result); + uint32_t pkts_per_burst = parse_int(mode, "pkts_per_burst", result); + + stream = new TrexStreamMultiBurst(port_id, stream_id, pkts_per_burst, pps, num_bursts, ibg_usec); + + + } else { + generate_parse_err(result, "bad stream type provided: '" + type + "'"); + } + + /* make sure we were able to allocate the memory */ + if (!stream) { + generate_internal_err(result, "unable to allocate memory"); + } + + return (stream); + +} + +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) { + + /* check packet size */ + if ( (stream->m_pkt.len < TrexStream::MIN_PKT_SIZE_BYTES) || (stream->m_pkt.len > TrexStream::MAX_PKT_SIZE_BYTES) ) { + std::stringstream ss; + ss << "bad packet size provided: should be between " << TrexStream::MIN_PKT_SIZE_BYTES << " and " << TrexStream::MAX_PKT_SIZE_BYTES; + delete stream; + generate_execute_err(result, ss.str()); + } + + /* port id should be between 0 and count - 1 */ + if (stream->m_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; + delete stream; + generate_execute_err(result, ss.str()); + } + + /* add the stream to the port's stream table */ + TrexStatelessPort * port = TrexStateless::get_instance().get_port_by_id(stream->m_port_id); + + /* does such a stream exists ? */ + if (port->get_stream_table()->get_stream_by_id(stream->m_stream_id)) { + std::stringstream ss; + ss << "stream " << stream->m_stream_id << " already exists"; + delete stream; + generate_execute_err(result, ss.str()); + } + +} + +/*************************** + * remove stream + * + **************************/ +trex_rpc_cmd_rc_e +TrexRpcCmdRemoveStream::_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 " << stream_id << " does not exists"; + generate_execute_err(result, ss.str()); + } + + port->get_stream_table()->remove_stream(stream); + delete stream; + + result["result"] = "ACK"; + + return (TREX_RPC_CMD_OK); +} + +/*************************** + * remove all streams + * for a port + * + **************************/ +trex_rpc_cmd_rc_e +TrexRpcCmdRemoveAllStreams::_run(const Json::Value ¶ms, Json::Value &result) { + uint8_t port_id = parse_byte(params, "port_id", result); + + if (port_id >= TrexStateless::get_instance().get_port_count()) { + std::stringstream ss; + ss << "invalid port id - should be between 0 and " << (int)TrexStateless::get_instance().get_port_count() - 1; + generate_execute_err(result, ss.str()); + } + + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); + port->get_stream_table()->remove_and_delete_all_streams(); + + result["result"] = "ACK"; + + return (TREX_RPC_CMD_OK); +} + +/*************************** + * get all streams configured + * on a specific port + * + **************************/ +trex_rpc_cmd_rc_e +TrexRpcCmdGetStreamList::_run(const Json::Value ¶ms, Json::Value &result) { + std::vector<uint32_t> stream_list; + + uint8_t port_id = parse_byte(params, "port_id", result); + + if (port_id >= TrexStateless::get_instance().get_port_count()) { + std::stringstream ss; + ss << "invalid port id - should be between 0 and " << (int)TrexStateless::get_instance().get_port_count() - 1; + generate_execute_err(result, ss.str()); + } + + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); + + port->get_stream_table()->get_stream_list(stream_list); + + Json::Value json_list = Json::arrayValue; + + for (auto stream_id : stream_list) { + json_list.append(stream_id); + } + + result["result"] = json_list; + + return (TREX_RPC_CMD_OK); +} + +/*************************** + * 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()); + } + + /* return the stored stream json (instead of decoding it all over again) */ + result["result"]["stream"] = stream->get_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::rc_e rc = port->start_traffic(); + + if (rc == TrexStatelessPort::RC_OK) { + result["result"] = "ACK"; + } else { + std::stringstream ss; + switch (rc) { + case TrexStatelessPort::RC_ERR_BAD_STATE_FOR_OP: + ss << "bad state for operations: port is either transmitting traffic or down"; + break; + case TrexStatelessPort::RC_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_cmd_test.cpp b/src/rpc-server/commands/trex_rpc_cmd_test.cpp new file mode 100644 index 00000000..3cdddd31 --- /dev/null +++ b/src/rpc-server/commands/trex_rpc_cmd_test.cpp @@ -0,0 +1,51 @@ +/* + 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 "trex_rpc_cmds.h" +#include <iostream> +#include <sstream> + +using namespace std; + +/** + * add command + * + */ +trex_rpc_cmd_rc_e +TrexRpcCmdTestAdd::_run(const Json::Value ¶ms, Json::Value &result) { + + result["result"] = parse_int(params, "x", result) + parse_int(params, "y", result); + + return (TREX_RPC_CMD_OK); +} + +/** + * sub command + * + * @author imarom (16-Aug-15) + */ +trex_rpc_cmd_rc_e +TrexRpcCmdTestSub::_run(const Json::Value ¶ms, Json::Value &result) { + + result["result"] = parse_int(params, "x", result) - parse_int(params, "y", result); + + 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 new file mode 100644 index 00000000..e261d1c6 --- /dev/null +++ b/src/rpc-server/commands/trex_rpc_cmds.h @@ -0,0 +1,104 @@ +/* + 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_RPC_CMD_H__ +#define __TREX_RPC_CMD_H__ + +#include <trex_rpc_cmd_api.h> +#include <json/json.h> + +class TrexStream; + +/* all the RPC commands decl. goes here */ + +/******************* test section ************/ + +/** + * syntactic sugar for creating a simple command + */ + +#define TREX_RPC_CMD_DEFINE_EXTENDED(class_name, cmd_name, param_count, needs_ownership, ext) \ + class class_name : public TrexRpcCommand { \ + public: \ + class_name () : TrexRpcCommand(cmd_name, param_count, needs_ownership) {} \ + protected: \ + virtual trex_rpc_cmd_rc_e _run(const Json::Value ¶ms, Json::Value &result); \ + ext \ + } + +#define TREX_RPC_CMD_DEFINE(class_name, cmd_name, param_count, needs_ownership) TREX_RPC_CMD_DEFINE_EXTENDED(class_name, cmd_name, param_count, needs_ownership, ;) + +/** + * test cmds + */ +TREX_RPC_CMD_DEFINE(TrexRpcCmdTestAdd, "test_add", 2, false); +TREX_RPC_CMD_DEFINE(TrexRpcCmdTestSub, "test_sub", 2, false); + +/** + * general cmds + */ +TREX_RPC_CMD_DEFINE(TrexRpcCmdPing, "ping", 0, false); +TREX_RPC_CMD_DEFINE(TrexRpcCmdGetCmds, "get_supported_cmds", 0, false); +TREX_RPC_CMD_DEFINE(TrexRpcCmdGetVersion, "get_version", 0, false); + +TREX_RPC_CMD_DEFINE_EXTENDED(TrexRpcCmdGetSysInfo, "get_system_info", 0, false, + +std::string get_cpu_model(); +void get_hostname(std::string &hostname); + +); + +/** + * ownership + */ +TREX_RPC_CMD_DEFINE(TrexRpcCmdGetOwner, "get_owner", 1, false); +TREX_RPC_CMD_DEFINE(TrexRpcCmdAcquire, "acquire", 3, false); +TREX_RPC_CMD_DEFINE(TrexRpcCmdRelease, "release", 1, true); + + +/** + * stream cmds + */ +TREX_RPC_CMD_DEFINE(TrexRpcCmdRemoveAllStreams, "remove_all_streams", 1, true); +TREX_RPC_CMD_DEFINE(TrexRpcCmdRemoveStream, "remove_stream", 2, true); + +TREX_RPC_CMD_DEFINE_EXTENDED(TrexRpcCmdAddStream, "add_stream", 3, true, + +/* 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); +); + + +TREX_RPC_CMD_DEFINE(TrexRpcCmdGetStreamList, "get_stream_list", 1, true); + +TREX_RPC_CMD_DEFINE(TrexRpcCmdGetStream, "get_stream", 2, true); + +TREX_RPC_CMD_DEFINE(TrexRpcCmdStartTraffic, "start_traffic", 1, true); +TREX_RPC_CMD_DEFINE(TrexRpcCmdStopTraffic, "stop_traffic", 1, true); + + + +#endif /* __TREX_RPC_CMD_H__ */ diff --git a/src/rpc-server/include/trex_rpc_cmd_api.h b/src/rpc-server/include/trex_rpc_cmd_api.h deleted file mode 100644 index c773b15f..00000000 --- a/src/rpc-server/include/trex_rpc_cmd_api.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - 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_RPC_CMD_API_H__ -#define __TREX_RPC_CMD_API_H__ - -#include <string> -#include <vector> -#include <json/json.h> - -/** - * interface for RPC command - * - * @author imarom (13-Aug-15) - */ -class TrexRpcCommand { -public: - - /** - * describe different types of rc for run() - */ - enum rpc_cmd_rc_e { - RPC_CMD_OK, - RPC_CMD_PARAM_COUNT_ERR = 1, - RPC_CMD_PARAM_PARSE_ERR, - RPC_CMD_INTERNAL_ERR - }; - - /** - * method name and params - */ - TrexRpcCommand(const std::string &method_name) : m_name(method_name) { - - } - - rpc_cmd_rc_e run(const Json::Value ¶ms, Json::Value &result) { - return _run(params, result); - } - - const std::string &get_name() { - return m_name; - } - - virtual ~TrexRpcCommand() {} - -protected: - - /** - * implemented by the dervied class - * - */ - virtual rpc_cmd_rc_e _run(const Json::Value ¶ms, Json::Value &result) = 0; - - /** - * error generating functions - * - */ - void genernate_err(Json::Value &result, const std::string &msg) { - result["specific_err"] = msg; - } - - void generate_err_param_count(Json::Value &result, int expected, int provided) { - std::stringstream ss; - ss << "method expects '" << expected << "' paramteres, '" << provided << "' provided"; - genernate_err(result, ss.str()); - } - - std::string m_name; -}; - -#endif /* __TREX_RPC_CMD_API_H__ */ - diff --git a/src/rpc-server/src/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/src/commands/trex_rpc_cmd_general.cpp deleted file mode 100644 index 193ce8db..00000000 --- a/src/rpc-server/src/commands/trex_rpc_cmd_general.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - 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 "trex_rpc_cmds.h" -#include <../linux_dpdk/version.h> -#include <trex_rpc_server_api.h> - -using namespace std; - -/** - * get status - * - */ -TrexRpcCommand::rpc_cmd_rc_e -TrexRpcCmdGetStatus::_run(const Json::Value ¶ms, Json::Value &result) { - - /* validate count */ - if (params.size() != 0) { - generate_err_param_count(result, 0, params.size()); - return (TrexRpcCommand::RPC_CMD_PARAM_COUNT_ERR); - } - - Json::Value §ion = result["result"]; - - section["general"]["version"] = VERSION_BUILD_NUM; - section["general"]["build_date"] = get_build_date(); - section["general"]["build_time"] = get_build_time(); - section["general"]["version_user"] = VERSION_USER; - section["general"]["uptime"] = TrexRpcServer::get_server_uptime(); - return (RPC_CMD_OK); -} - diff --git a/src/rpc-server/src/commands/trex_rpc_cmd_test.cpp b/src/rpc-server/src/commands/trex_rpc_cmd_test.cpp deleted file mode 100644 index e2dc8959..00000000 --- a/src/rpc-server/src/commands/trex_rpc_cmd_test.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - 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 "trex_rpc_cmds.h" -#include <iostream> -#include <sstream> -#include <trex_rpc_cmds_table.h> - -using namespace std; - -/** - * add command - * - */ -TrexRpcCommand::rpc_cmd_rc_e -TrexRpcCmdTestAdd::_run(const Json::Value ¶ms, Json::Value &result) { - - const Json::Value &x = params["x"]; - const Json::Value &y = params["y"]; - - /* validate count */ - if (params.size() != 2) { - generate_err_param_count(result, 2, params.size()); - return (TrexRpcCommand::RPC_CMD_PARAM_COUNT_ERR); - } - - /* check we have all the required paramters */ - if (!x.isInt()) { - genernate_err(result, "'x' is either missing or not an integer"); - return (TrexRpcCommand::RPC_CMD_PARAM_PARSE_ERR); - } - - if (!y.isInt()) { - genernate_err(result, "'y' is either missing or not an integer"); - return (TrexRpcCommand::RPC_CMD_PARAM_PARSE_ERR); - } - - result["result"] = x.asInt() + y.asInt(); - return (RPC_CMD_OK); -} - -/** - * sub command - * - * @author imarom (16-Aug-15) - */ -TrexRpcCommand::rpc_cmd_rc_e -TrexRpcCmdTestSub::_run(const Json::Value ¶ms, Json::Value &result) { - - const Json::Value &x = params["x"]; - const Json::Value &y = params["y"]; - - /* validate count */ - if (params.size() != 2) { - generate_err_param_count(result, 2, params.size()); - return (TrexRpcCommand::RPC_CMD_PARAM_COUNT_ERR); - } - - /* check we have all the required paramters */ - if (!x.isInt() || !y.isInt()) { - return (TrexRpcCommand::RPC_CMD_PARAM_PARSE_ERR); - } - - result["result"] = x.asInt() - y.asInt(); - return (RPC_CMD_OK); -} - -/** - * ping command - */ -TrexRpcCommand::rpc_cmd_rc_e -TrexRpcCmdPing::_run(const Json::Value ¶ms, Json::Value &result) { - - /* validate count */ - if (params.size() != 0) { - generate_err_param_count(result, 0, params.size()); - return (TrexRpcCommand::RPC_CMD_PARAM_COUNT_ERR); - } - - result["result"] = "ACK"; - return (RPC_CMD_OK); -} - -/** - * query command - */ -TrexRpcCommand::rpc_cmd_rc_e -TrexRpcCmdGetReg::_run(const Json::Value ¶ms, Json::Value &result) { - vector<string> cmds; - - /* validate count */ - if (params.size() != 0) { - generate_err_param_count(result, 0, params.size()); - return (TrexRpcCommand::RPC_CMD_PARAM_COUNT_ERR); - } - - - TrexRpcCommandsTable::get_instance().query(cmds); - - Json::Value test = Json::arrayValue; - for (auto cmd : cmds) { - test.append(cmd); - } - - result["result"] = test; - - return (RPC_CMD_OK); -} - diff --git a/src/rpc-server/src/commands/trex_rpc_cmds.h b/src/rpc-server/src/commands/trex_rpc_cmds.h deleted file mode 100644 index e37e1cda..00000000 --- a/src/rpc-server/src/commands/trex_rpc_cmds.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - 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_RPC_CMD_H__ -#define __TREX_RPC_CMD_H__ - -#include <trex_rpc_cmd_api.h> -#include <json/json.h> - -/* all the RPC commands decl. goes here */ - -/******************* test section ************/ - -/** - * add - * - */ -class TrexRpcCmdTestAdd : public TrexRpcCommand { -public: - TrexRpcCmdTestAdd() : TrexRpcCommand("test_add") {} -protected: - virtual rpc_cmd_rc_e _run(const Json::Value ¶ms, Json::Value &result); -}; - -/** - * sub - * - */ -class TrexRpcCmdTestSub : public TrexRpcCommand { -public: - TrexRpcCmdTestSub() : TrexRpcCommand("test_sub") {} ; -protected: - virtual rpc_cmd_rc_e _run(const Json::Value ¶ms, Json::Value &result); -}; - -/** - * ping - * - */ -class TrexRpcCmdPing : public TrexRpcCommand { -public: - TrexRpcCmdPing() : TrexRpcCommand("ping") {}; -protected: - virtual rpc_cmd_rc_e _run(const Json::Value ¶ms, Json::Value &result); -}; - -/** - * get all registered commands - * - */ -class TrexRpcCmdGetReg : public TrexRpcCommand { -public: - TrexRpcCmdGetReg() : TrexRpcCommand("get_reg_cmds") {}; -protected: - virtual rpc_cmd_rc_e _run(const Json::Value ¶ms, Json::Value &result); -}; - -/** - * get status - * - */ -class TrexRpcCmdGetStatus : public TrexRpcCommand { -public: - TrexRpcCmdGetStatus() : TrexRpcCommand("get_status") {}; -protected: - virtual rpc_cmd_rc_e _run(const Json::Value ¶ms, Json::Value &result); -}; - - -/**************** test section end *************/ -#endif /* __TREX_RPC_CMD_H__ */ diff --git a/src/rpc-server/trex_rpc_cmd.cpp b/src/rpc-server/trex_rpc_cmd.cpp new file mode 100644 index 00000000..6c355e70 --- /dev/null +++ b/src/rpc-server/trex_rpc_cmd.cpp @@ -0,0 +1,317 @@ +/* + 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 <trex_rpc_cmd_api.h> +#include <trex_rpc_server_api.h> +#include <trex_stateless_api.h> + +trex_rpc_cmd_rc_e +TrexRpcCommand::run(const Json::Value ¶ms, Json::Value &result) { + trex_rpc_cmd_rc_e rc; + + /* the internal run can throw a parser error / other error */ + try { + + check_param_count(params, m_param_count, result); + + if (m_needs_ownership) { + verify_ownership(params, result); + } + + /* run the command itself*/ + rc = _run(params, result); + + } catch (TrexRpcCommandException &e) { + return e.get_rc(); + } + + return (rc); +} + +void +TrexRpcCommand::check_param_count(const Json::Value ¶ms, int expected, Json::Value &result) { + + if (params.size() != expected) { + std::stringstream ss; + ss << "method expects '" << expected << "' paramteres, '" << params.size() << "' provided"; + generate_parse_err(result, ss.str()); + } +} + +void +TrexRpcCommand::verify_ownership(const Json::Value ¶ms, Json::Value &result) { + std::string handler = parse_string(params, "handler", result); + uint8_t port_id = parse_port(params, result); + + TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); + + if (!port->verify_owner_handler(handler)) { + generate_execute_err(result, "invalid handler provided. please pass the handler given when calling 'acquire' or take ownership"); + } +} + +uint8_t +TrexRpcCommand::parse_port(const Json::Value ¶ms, Json::Value &result) { + uint8_t port_id = parse_byte(params, "port_id", result); + validate_port_id(port_id, result); + + return (port_id); +} + +void +TrexRpcCommand::validate_port_id(uint8_t port_id, Json::Value &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()); + } +} + +const char * +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: + return "int"; + case FIELD_TYPE_DOUBLE: + return "double"; + case FIELD_TYPE_OBJ: + return "object"; + case FIELD_TYPE_STR: + return "string"; + case FIELD_TYPE_ARRAY: + return "array"; + + default: + return "UNKNOWN"; + } +} + +const char * +TrexRpcCommand::json_type_to_name(const Json::Value &value) { + + switch(value.type()) { + case Json::nullValue: + return "null"; + case Json::intValue: + return "int"; + case Json::uintValue: + return "uint"; + case Json::realValue: + return "real"; + case Json::stringValue: + return "string"; + case Json::booleanValue: + return "boolean"; + case Json::arrayValue: + return "array"; + case Json::objectValue: + return "object"; + + default: + return "UNKNOWN"; + } + +} + +uint8_t +TrexRpcCommand::parse_byte(const Json::Value &parent, const std::string &name, Json::Value &result) { + check_field_type(parent, name, FIELD_TYPE_BYTE, result); + return parent[name].asUInt(); +} + +uint8_t +TrexRpcCommand::parse_byte(const Json::Value &parent, int index, Json::Value &result) { + check_field_type(parent, index, FIELD_TYPE_BYTE, result); + 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); + return parent[name].asInt(); +} + +bool +TrexRpcCommand::parse_bool(const Json::Value &parent, const std::string &name, Json::Value &result) { + check_field_type(parent, name, FIELD_TYPE_BOOL, result); + return parent[name].asBool(); +} + +double +TrexRpcCommand::parse_double(const Json::Value &parent, const std::string &name, Json::Value &result) { + check_field_type(parent, name, FIELD_TYPE_DOUBLE, result); + return parent[name].asDouble(); +} + +const std::string +TrexRpcCommand::parse_string(const Json::Value &parent, const std::string &name, Json::Value &result) { + check_field_type(parent, name, FIELD_TYPE_STR, result); + return parent[name].asString(); +} + +const Json::Value & +TrexRpcCommand::parse_object(const Json::Value &parent, const std::string &name, Json::Value &result) { + check_field_type(parent, name, FIELD_TYPE_OBJ, result); + return parent[name]; +} + +const Json::Value & +TrexRpcCommand::parse_array(const Json::Value &parent, const std::string &name, Json::Value &result) { + check_field_type(parent, name, FIELD_TYPE_ARRAY, result); + return parent[name]; +} + +/** + * for index element (array) + */ +void +TrexRpcCommand::check_field_type(const Json::Value &parent, int index, field_type_e type, Json::Value &result) { + + /* should never get here without parent being array */ + if (!parent.isArray()) { + throw TrexRpcException("internal parsing error"); + } + + const Json::Value &field = parent[index]; + + std::stringstream ss; + ss << "array element: " << (index + 1) << " "; + check_field_type_common(field, ss.str(), type, result); +} + +void +TrexRpcCommand::check_field_type(const Json::Value &parent, const std::string &name, field_type_e type, Json::Value &result) { + /* should never get here without parent being object */ + if (!parent.isObject()) { + throw TrexRpcException("internal parsing error"); + } + + const Json::Value &field = parent[name]; + check_field_type_common(field, name, type, result); +} +void +TrexRpcCommand::check_field_type_common(const Json::Value &field, const std::string &name, field_type_e type, Json::Value &result) { + std::stringstream ss; + + /* first check if field exists */ + if (field == Json::Value::null) { + ss << "field '" << name << "' is missing"; + generate_parse_err(result, ss.str()); + } + + bool rc = true; + + switch (type) { + case FIELD_TYPE_BYTE: + if ( (!field.isUInt()) || (field.asInt() > 0xFF)) { + rc = false; + } + break; + + case FIELD_TYPE_UINT16: + if ( (!field.isUInt()) || (field.asInt() > 0xFFFF)) { + rc = false; + } + break; + + case FIELD_TYPE_BOOL: + if (!field.isBool()) { + rc = false; + } + break; + + case FIELD_TYPE_INT: + if (!field.isInt()) { + rc = false; + } + break; + + case FIELD_TYPE_DOUBLE: + if (!field.isDouble()) { + rc = false; + } + break; + + case FIELD_TYPE_OBJ: + if (!field.isObject()) { + rc = false; + } + break; + + case FIELD_TYPE_STR: + if (!field.isString()) { + rc = false; + } + break; + + case FIELD_TYPE_ARRAY: + if (!field.isArray()) { + rc = false; + } + break; + + default: + throw TrexRpcException("unhandled type"); + break; + + } + if (!rc) { + ss << "error at offset: " << field.getOffsetStart() << " - '" << name << "' is '" << json_type_to_name(field) << "', expecting '" << type_to_str(type) << "'"; + generate_parse_err(result, ss.str()); + } + +} + +void +TrexRpcCommand::generate_parse_err(Json::Value &result, const std::string &msg) { + result["specific_err"] = msg; + throw (TrexRpcCommandException(TREX_RPC_CMD_PARSE_ERR)); +} + +void +TrexRpcCommand::generate_internal_err(Json::Value &result, const std::string &msg) { + result["specific_err"] = msg; + throw (TrexRpcCommandException(TREX_RPC_CMD_INTERNAL_ERR)); +} + +void +TrexRpcCommand::generate_execute_err(Json::Value &result, const std::string &msg) { + result["specific_err"] = msg; + throw (TrexRpcCommandException(TREX_RPC_CMD_EXECUTE_ERR)); +} + diff --git a/src/rpc-server/trex_rpc_cmd_api.h b/src/rpc-server/trex_rpc_cmd_api.h new file mode 100644 index 00000000..3c718eaa --- /dev/null +++ b/src/rpc-server/trex_rpc_cmd_api.h @@ -0,0 +1,241 @@ +/* + 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_RPC_CMD_API_H__ +#define __TREX_RPC_CMD_API_H__ + +#include <string> +#include <vector> +#include <json/json.h> +#include <trex_rpc_exception_api.h> + +/** + * describe different types of rc for run() + */ +typedef enum trex_rpc_cmd_rc_ { + TREX_RPC_CMD_OK, + TREX_RPC_CMD_PARSE_ERR, + TREX_RPC_CMD_EXECUTE_ERR, + TREX_RPC_CMD_INTERNAL_ERR +} trex_rpc_cmd_rc_e; + +/** + * simple exception for RPC command processing + * + * @author imarom (23-Aug-15) + */ +class TrexRpcCommandException : TrexRpcException { +public: + TrexRpcCommandException(trex_rpc_cmd_rc_e rc) : m_rc(rc) { + + } + + trex_rpc_cmd_rc_e get_rc() { + return m_rc; + + } + +protected: + trex_rpc_cmd_rc_e m_rc; +}; + +/** + * interface for RPC command + * + * @author imarom (13-Aug-15) + */ +class TrexRpcCommand { +public: + + /** + * method name and params + */ + TrexRpcCommand(const std::string &method_name, int param_count, bool needs_ownership) : + m_name(method_name), + m_param_count(param_count), + m_needs_ownership(needs_ownership) { + + /* if needs ownership - another field is needed (handler) */ + if (m_needs_ownership) { + m_param_count++; + } + } + + /** + * entry point for executing RPC command + * + */ + trex_rpc_cmd_rc_e run(const Json::Value ¶ms, Json::Value &result); + + const std::string &get_name() { + return m_name; + } + + virtual ~TrexRpcCommand() {} + +protected: + + /** + * different types of fields + */ + enum field_type_e { + FIELD_TYPE_BYTE, + FIELD_TYPE_UINT16, + FIELD_TYPE_INT, + FIELD_TYPE_DOUBLE, + FIELD_TYPE_BOOL, + FIELD_TYPE_STR, + FIELD_TYPE_OBJ, + FIELD_TYPE_ARRAY + }; + + /** + * implemented by the dervied class + * + */ + virtual trex_rpc_cmd_rc_e _run(const Json::Value ¶ms, Json::Value &result) = 0; + + /** + * check param count + */ + void check_param_count(const Json::Value ¶ms, int expected, Json::Value &result); + + /** + * verify ownership + * + */ + void verify_ownership(const Json::Value ¶ms, Json::Value &result); + + /** + * validate port id + * + */ + void validate_port_id(uint8_t port_id, Json::Value &result); + + /** + * parse functions + * + */ + 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); + const std::string parse_string(const Json::Value &parent, const std::string &name, Json::Value &result); + const Json::Value & parse_object(const Json::Value &parent, const std::string &name, Json::Value &result); + 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); + const std::string parse_string(const Json::Value &parent, int index, Json::Value &result); + 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); + + /* shortcut for parsing port id */ + uint8_t parse_port(const Json::Value ¶ms, Json::Value &result); + + /** + * parse a field from choices + * + */ + template<typename T> T parse_choice(const Json::Value ¶ms, const std::string &name, std::initializer_list<T> 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); + + /* dummy return value - does not matter, the above will throw exception */ + return (*choices.begin()); + } + + /** + * check field type + * + */ + void check_field_type(const Json::Value &parent, const std::string &name, field_type_e type, Json::Value &result); + void check_field_type(const Json::Value &parent, int index, field_type_e type, Json::Value &result); + void check_field_type_common(const Json::Value &field, const std::string &name, field_type_e type, Json::Value &result); + + /** + * error generating functions + * + */ + void generate_parse_err(Json::Value &result, const std::string &msg); + + + /** + * method execute error + * + */ + void generate_execute_err(Json::Value &result, const std::string &msg); + + /** + * internal error + * + */ + void generate_internal_err(Json::Value &result, const std::string &msg); + + + /** + * translate enum to string + * + */ + const char * type_to_str(field_type_e type); + + /** + * translate JSON values to string + * + */ + const char * json_type_to_name(const Json::Value &value); + + /* RPC command name */ + std::string m_name; + int m_param_count; + bool m_needs_ownership; +}; + +#endif /* __TREX_RPC_CMD_API_H__ */ + diff --git a/src/rpc-server/src/trex_rpc_cmds_table.cpp b/src/rpc-server/trex_rpc_cmds_table.cpp index 04a56389..170f0de1 100644 --- a/src/rpc-server/src/trex_rpc_cmds_table.cpp +++ b/src/rpc-server/trex_rpc_cmds_table.cpp @@ -30,9 +30,25 @@ TrexRpcCommandsTable::TrexRpcCommandsTable() { /* add the test command (for gtest) */ register_command(new TrexRpcCmdTestAdd()); register_command(new TrexRpcCmdTestSub()); + + + /* general */ register_command(new TrexRpcCmdPing()); - register_command(new TrexRpcCmdGetReg()); - register_command(new TrexRpcCmdGetStatus()); + register_command(new TrexRpcCmdGetCmds()); + register_command(new TrexRpcCmdGetVersion()); + register_command(new TrexRpcCmdGetSysInfo()); + register_command(new TrexRpcCmdGetOwner()); + register_command(new TrexRpcCmdAcquire()); + register_command(new TrexRpcCmdRelease()); + + /* stream commands */ + register_command(new TrexRpcCmdAddStream()); + 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/include/trex_rpc_cmds_table.h b/src/rpc-server/trex_rpc_cmds_table.h index a41944f1..a41944f1 100644 --- a/src/rpc-server/include/trex_rpc_cmds_table.h +++ b/src/rpc-server/trex_rpc_cmds_table.h diff --git a/src/rpc-server/include/trex_rpc_exception_api.h b/src/rpc-server/trex_rpc_exception_api.h index 8783c219..e349b980 100644 --- a/src/rpc-server/include/trex_rpc_exception_api.h +++ b/src/rpc-server/trex_rpc_exception_api.h @@ -32,6 +32,9 @@ limitations under the License. class TrexRpcException : public std::runtime_error { public: + TrexRpcException() : std::runtime_error("") { + + } TrexRpcException(const std::string &what) : std::runtime_error(what) { } }; diff --git a/src/rpc-server/src/trex_rpc_jsonrpc_v2_parser.cpp b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp index be1eb2f8..9d9de53a 100644 --- a/src/rpc-server/src/trex_rpc_jsonrpc_v2_parser.cpp +++ b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp @@ -37,7 +37,10 @@ enum { JSONRPC_V2_ERR_INVALID_REQ = -32600, JSONRPC_V2_ERR_METHOD_NOT_FOUND = -32601, JSONRPC_V2_ERR_INVALID_PARAMS = -32602, - JSONRPC_V2_ERR_INTERNAL_ERROR = -32603 + JSONRPC_V2_ERR_INTERNAL_ERROR = -32603, + + /* specific server errors */ + JSONRPC_V2_ERR_EXECUTE_ERROR = -32000, }; @@ -71,21 +74,26 @@ public: virtual void _execute(Json::Value &response) { Json::Value result; - TrexRpcCommand::rpc_cmd_rc_e rc = m_cmd->run(m_params, result); + trex_rpc_cmd_rc_e rc = m_cmd->run(m_params, result); switch (rc) { - case TrexRpcCommand::RPC_CMD_OK: + case TREX_RPC_CMD_OK: response["result"] = result["result"]; break; - case TrexRpcCommand::RPC_CMD_PARAM_COUNT_ERR: - case TrexRpcCommand::RPC_CMD_PARAM_PARSE_ERR: + case TREX_RPC_CMD_PARSE_ERR: response["error"]["code"] = JSONRPC_V2_ERR_INVALID_PARAMS; response["error"]["message"] = "Bad paramters for method"; response["error"]["specific_err"] = result["specific_err"]; break; - case TrexRpcCommand::RPC_CMD_INTERNAL_ERR: + case TREX_RPC_CMD_EXECUTE_ERR: + response["error"]["code"] = JSONRPC_V2_ERR_EXECUTE_ERROR; + response["error"]["message"] = "Failed To Execute Method"; + response["error"]["specific_err"] = result["specific_err"]; + break; + + case TREX_RPC_CMD_INTERNAL_ERR: response["error"]["code"] = JSONRPC_V2_ERR_INTERNAL_ERROR; response["error"]["message"] = "Internal Server Error"; response["error"]["specific_err"] = result["specific_err"]; @@ -192,3 +200,36 @@ void TrexJsonRpcV2Parser::parse_single_request(Json::Value &request, commands.push_back(new JsonRpcMethod(msg_id, rpc_cmd, request["params"])); } +/** + * tries to pretty a JSON str + * + * @author imarom (03-Sep-15) + * + * @param json_str + * + * @return std::string + */ +std::string TrexJsonRpcV2Parser::pretty_json_str(const std::string &json_str) { + Json::Reader reader; + Json::Value value; + + /* basic JSON parsing */ + bool rc = reader.parse(json_str, value, false); + if (!rc) { + /* duplicate the soruce */ + return json_str; + } + + /* successfully parsed */ + Json::StyledWriter writer; + return writer.write(value); +} + +void +TrexJsonRpcV2Parser::generate_common_error(Json::Value &json, const std::string &specific_err) { + JsonRpcError err(Json::Value::null, JSONRPC_V2_ERR_INTERNAL_ERROR, specific_err, true); + + err.execute(json); + +} + diff --git a/src/rpc-server/include/trex_rpc_jsonrpc_v2_parser.h b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h index 3367ad6a..0563f21d 100644 --- a/src/rpc-server/include/trex_rpc_jsonrpc_v2_parser.h +++ b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h @@ -79,6 +79,23 @@ public: */ void parse(std::vector<TrexJsonRpcV2ParsedObject *> &commands); + /** + * will generate a valid JSON RPC v2 error message with + * generic error code and message + * + * @author imarom (16-Sep-15) + * + */ + static void generate_common_error(Json::Value &json, const std::string &specific_err); + + /** + * *tries* to generate a pretty string from JSON + * if json_str is not a valid JSON string + * it will duplicate the source + * + */ + static std::string pretty_json_str(const std::string &json_str); + private: /** diff --git a/src/rpc-server/src/trex_rpc_req_resp_server.cpp b/src/rpc-server/trex_rpc_req_resp_server.cpp index 7484758d..3d52686c 100644 --- a/src/rpc-server/src/trex_rpc_req_resp_server.cpp +++ b/src/rpc-server/trex_rpc_req_resp_server.cpp @@ -82,10 +82,17 @@ void TrexRpcServerReqRes::_rpc_thread_cb() { } } + if (msg_size >= sizeof(m_msg_buffer)) { + std::stringstream ss; + ss << "RPC request of '" << msg_size << "' exceeds maximum message size which is '" << sizeof(m_msg_buffer) << "'"; + handle_server_error(ss.str()); + continue; + } + /* transform it to a string */ std::string request((const char *)m_msg_buffer, msg_size); - verbose_msg("Server Received: " + request); + verbose_json("Server Received: ", TrexJsonRpcV2Parser::pretty_json_str(request)); handle_request(request); } @@ -110,6 +117,7 @@ void TrexRpcServerReqRes::_stop_rpc_thread() { */ void TrexRpcServerReqRes::handle_request(const std::string &request) { std::vector<TrexJsonRpcV2ParsedObject *> commands; + Json::FastWriter writer; Json::Value response; @@ -139,8 +147,28 @@ void TrexRpcServerReqRes::handle_request(const std::string &request) { response_str = writer.write(response); } - verbose_msg("Server Replied: " + response_str); + verbose_json("Server Replied: ", response_str); zmq_send(m_socket, response_str.c_str(), response_str.size(), 0); } + +/** + * handles a server error + * + */ +void +TrexRpcServerReqRes::handle_server_error(const std::string &specific_err) { + Json::FastWriter writer; + Json::Value response; + + /* generate error */ + TrexJsonRpcV2Parser::generate_common_error(response, specific_err); + + /* write the JSON to string and sever on ZMQ */ + std::string response_str = writer.write(response); + + verbose_json("Server Replied: ", response_str); + + zmq_send(m_socket, response_str.c_str(), response_str.size(), 0); +} diff --git a/src/rpc-server/include/trex_rpc_req_resp_server.h b/src/rpc-server/trex_rpc_req_resp_server.h index f12d0540..7c1d66d1 100644 --- a/src/rpc-server/include/trex_rpc_req_resp_server.h +++ b/src/rpc-server/trex_rpc_req_resp_server.h @@ -39,9 +39,11 @@ protected: void _stop_rpc_thread(); private: + void handle_request(const std::string &request); + void handle_server_error(const std::string &specific_err); - static const int RPC_MAX_MSG_SIZE = 2048; + static const int RPC_MAX_MSG_SIZE = (20 * 1024); void *m_context; void *m_socket; uint8_t m_msg_buffer[RPC_MAX_MSG_SIZE]; diff --git a/src/rpc-server/src/trex_rpc_server.cpp b/src/rpc-server/trex_rpc_server.cpp index 366bfc5b..6b8c200d 100644 --- a/src/rpc-server/src/trex_rpc_server.cpp +++ b/src/rpc-server/trex_rpc_server.cpp @@ -21,6 +21,7 @@ limitations under the License. #include <trex_rpc_server_api.h> #include <trex_rpc_req_resp_server.h> +#include <trex_rpc_jsonrpc_v2_parser.h> #include <unistd.h> #include <zmq.h> #include <sstream> @@ -47,6 +48,10 @@ void TrexRpcServerInterface::verbose_msg(const std::string &msg) { std::cout << "[verbose][" << m_name << "] " << msg << "\n"; } +void TrexRpcServerInterface::verbose_json(const std::string &msg, const std::string &json_str) { + verbose_msg(msg + "\n\n" + TrexJsonRpcV2Parser::pretty_json_str(json_str)); +} + /** * starts a RPC specific server * diff --git a/src/rpc-server/include/trex_rpc_server_api.h b/src/rpc-server/trex_rpc_server_api.h index 6bb81c73..06bbe10c 100644 --- a/src/rpc-server/include/trex_rpc_server_api.h +++ b/src/rpc-server/trex_rpc_server_api.h @@ -115,6 +115,13 @@ protected: */ void verbose_msg(const std::string &msg); + /** + * prints a verbose message with a JSON to be converted to + * string + * + */ + void verbose_json(const std::string &msg, const std::string &json_str); + TrexRpcServerConfig m_cfg; bool m_is_running; bool m_is_verbose; @@ -156,10 +163,18 @@ public: return s_server_uptime; } + + + private: + static std::string generate_handler(); + std::vector<TrexRpcServerInterface *> m_servers; bool m_verbose; static const std::string s_server_uptime; + + static std::string s_owner; + static std::string s_owner_handler; }; #endif /* __TREX_RPC_SERVER_API_H__ */ diff --git a/src/rpc-server/src/trex_rpc_server_mock.cpp b/src/rpc-server/trex_rpc_server_mock.cpp index fd4f051c..835e28b8 100644 --- a/src/rpc-server/src/trex_rpc_server_mock.cpp +++ b/src/rpc-server/trex_rpc_server_mock.cpp @@ -20,6 +20,8 @@ limitations under the License. */ #include <trex_rpc_server_api.h> +#include <trex_stateless_api.h> + #include <iostream> #include <unistd.h> @@ -42,9 +44,12 @@ 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") || (argc != 2) ) { + if (string(argv[1]) != "--ut") { cout << "\n[Usage] " << argv[0] << ": " << " [--ut]\n\n"; exit(-1); } |