diff options
60 files changed, 2993 insertions, 856 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 57d19820..3cda10ac 100755 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1,4 +1,4 @@ -Hanoh haim +Hanoch haim hhaim@cisco.com Dave Johnson Wenxian Li Dan Klein @@ -1,4 +1,4 @@ -v1.77 +v1.78 diff --git a/linux/ws_main.py b/linux/ws_main.py index 8ad3e5ba..eac46ac7 100755 --- a/linux/ws_main.py +++ b/linux/ws_main.py @@ -142,15 +142,18 @@ net_src = SrcGroup(dir='src/common/Network/Packet', # stateless code stateless_src = SrcGroup(dir='src/stateless/', - src_list=['trex_stream.cpp', - 'trex_stream_vm.cpp', - 'trex_stateless.cpp', + src_list=['cp/trex_stream.cpp', + 'cp/trex_stream_vm.cpp', + 'cp/trex_stateless.cpp', + 'cp/trex_stateless_port.cpp', + 'dp/trex_stateless_dp_core.cpp' ]) # RPC code rpc_server_src = SrcGroup(dir='src/rpc-server/', src_list=[ 'trex_rpc_server.cpp', 'trex_rpc_req_resp_server.cpp', + 'trex_rpc_async_server.cpp', 'trex_rpc_jsonrpc_v2_parser.cpp', 'trex_rpc_cmds_table.cpp', 'trex_rpc_cmd.cpp', @@ -162,10 +165,12 @@ rpc_server_src = SrcGroup(dir='src/rpc-server/', ]) # RPC mock server (test) -rpc_server_mock_src = SrcGroup(dir='src/rpc-server/', +rpc_server_mock_src = SrcGroup(dir='src/mock/', src_list=[ 'trex_rpc_server_mock.cpp', '../gtest/rpc_test.cpp', + '../pal/linux/mbuf.cpp', + '../os_time.cpp', ]) # JSON package @@ -233,8 +238,10 @@ cxxflags_base =['-DWIN_UCODE_SIM', includes_path =''' ../src/pal/linux/ ../src/ + ../src/mock/ ../src/rpc-server/ - ../src/stateless/ + ../src/stateless/cp/ + ../src/stateless/dp/ ../external_libs/json/ ../external_libs/zmq/include/ ../external_libs/yaml-cpp/include/ diff --git a/linux_dpdk/ws_main.py b/linux_dpdk/ws_main.py index 6aad508a..61a9d4f3 100755 --- a/linux_dpdk/ws_main.py +++ b/linux_dpdk/ws_main.py @@ -141,6 +141,7 @@ rpc_server_src = SrcGroup(dir='src/rpc-server/', src_list=[ 'trex_rpc_server.cpp', 'trex_rpc_req_resp_server.cpp', + 'trex_rpc_async_server.cpp', 'trex_rpc_jsonrpc_v2_parser.cpp', 'trex_rpc_cmds_table.cpp', 'trex_rpc_cmd.cpp', @@ -148,8 +149,17 @@ rpc_server_src = SrcGroup(dir='src/rpc-server/', 'commands/trex_rpc_cmd_test.cpp', 'commands/trex_rpc_cmd_general.cpp', 'commands/trex_rpc_cmd_stream.cpp', + ]) +# stateless code +stateless_src = SrcGroup(dir='src/stateless/', + src_list=['cp/trex_stream.cpp', + 'cp/trex_stream_vm.cpp', + 'cp/trex_stateless.cpp', + 'cp/trex_stateless_port.cpp', + 'dp/trex_stateless_dp_core.cpp' + ]) # JSON package json_src = SrcGroup(dir='external_libs/json', src_list=[ @@ -346,6 +356,9 @@ bp =SrcGroups([ cmn_src , net_src , yaml_src, + rpc_server_src, + json_src, + stateless_src, version_src ]); @@ -400,7 +413,8 @@ includes_path =''' ../src/pal/linux_dpdk/ ../src/ ../src/rpc-server/ - ../src/stateless/ + ../src/stateless/cp/ + ../src/stateless/dp/ ../external_libs/yaml-cpp/include/ ../external_libs/zmq/include/ diff --git a/scripts/automation/readme.txt b/scripts/automation/readme.txt index 2541a1a3..152eee16 100755 --- a/scripts/automation/readme.txt +++ b/scripts/automation/readme.txt @@ -1,7 +1,7 @@ README - trex_perf.py ===================== -This script uses the T-Rex RESTfull client-server conrtol plane achitecture and tries to find the maximum M (platform factor) for trex before hitting one of two stopping conditions: +This script uses the TRex RESTfull client-server conrtol plane achitecture and tries to find the maximum M (platform factor) for trex before hitting one of two stopping conditions: (*) Packet drops (*) High latency. Since high latency can change from one platform to another, and might suffer from kickoff peak (espicially at VM), it is the user responsibility to provide the latency condition. @@ -9,7 +9,7 @@ This script uses the T-Rex RESTfull client-server conrtol plane achitecture and please note that '-f' and '-c' options are mandatory. -Also, this is the user's responsibility to make sure a T-Rex is running, listening to relevant client request coming from this script. +Also, this is the user's responsibility to make sure a TRex is running, listening to relevant client request coming from this script. example for finding max M (between 10 to 100) with imix_fast_1g.yaml traffic profile: ./trex_perf.py -m 10 100 -c config/trex-hhaim.cfg all drop -f cap2/imix_fast_1g.yaml diff --git a/scripts/automation/report_template.html b/scripts/automation/report_template.html index 779d5429..ccd5388d 100755 --- a/scripts/automation/report_template.html +++ b/scripts/automation/report_template.html @@ -76,7 +76,7 @@ vertical-align:top; <body> <H1> -T-Rex Performance Report +TRex Performance Report </H1> <H2> diff --git a/scripts/automation/trex_control_plane/client/trex_adv_client.py b/scripts/automation/trex_control_plane/client/trex_adv_client.py index b3fe3dad..bf7ccf58 100755 --- a/scripts/automation/trex_control_plane/client/trex_adv_client.py +++ b/scripts/automation/trex_control_plane/client/trex_adv_client.py @@ -8,7 +8,7 @@ class CTRexAdvClient(trex_client.CTRexClient): super(CTRexAdvClient, self).__init__(trex_host, max_history_size, trex_daemon_port, trex_zmq_port, verbose) pass - # T-REX KIWI advanced methods + # TRex KIWI advanced methods def start_quick_trex(self, pcap_file, d, delay, dual, ipv6, times, interfaces): try: return self.server.start_quick_trex(pcap_file = pcap_file, duration = d, dual = dual, delay = delay, ipv6 = ipv6, times = times, interfaces = interfaces) diff --git a/scripts/automation/trex_control_plane/client_utils/external_packages.py b/scripts/automation/trex_control_plane/client_utils/external_packages.py index 4b10609b..e2bb37a5 100755 --- a/scripts/automation/trex_control_plane/client_utils/external_packages.py +++ b/scripts/automation/trex_control_plane/client_utils/external_packages.py @@ -8,7 +8,8 @@ ROOT_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir)) PATH_TO_PYTHON_LIB = os.path.abspath(os.path.join(ROOT_PATH, os.pardir, os.pardir, 'external_libs')) CLIENT_UTILS_MODULES = ['zmq', - 'dpkt-1.8.6' + 'dpkt-1.8.6', + 'PyYAML-3.01/lib' ] def import_client_utils_modules(): diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index 163c6923..ed14e6f8 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -6,9 +6,6 @@ import json import general_utils import re from time import sleep -from collections import namedtuple - -CmdResponse = namedtuple('CmdResponse', ['success', 'data']) class bcolors: BLUE = '\033[94m' @@ -26,12 +23,12 @@ class BatchMessage(object): self.rpc_client = rpc_client self.batch_list = [] - def add (self, method_name, params={}): + def add (self, method_name, params = {}): id, msg = self.rpc_client.create_jsonrpc_v2(method_name, params, encode = False) self.batch_list.append(msg) - def invoke(self, block = False): + def invoke (self, block = False): if not self.rpc_client.connected: return False, "Not connected to server" @@ -39,9 +36,9 @@ class BatchMessage(object): rc, resp_list = self.rpc_client.send_raw_msg(msg, block = False) if len(self.batch_list) == 1: - return CmdResponse(True, [CmdResponse(rc, resp_list)]) + return True, [(rc, resp_list)] else: - return CmdResponse(rc, resp_list) + return rc, resp_list # JSON RPC v2.0 client @@ -130,7 +127,7 @@ class JsonRpcClient(object): self.socket.send(msg, flags = zmq.NOBLOCK) except zmq.error.ZMQError as e: self.disconnect() - return CmdResponse(False, "Failed To Get Send Message") + return False, "Failed To Get Send Message" got_response = False @@ -148,7 +145,7 @@ class JsonRpcClient(object): if not got_response: self.disconnect() - return CmdResponse(False, "Failed To Get Server Response") + return False, "Failed To Get Server Response" self.verbose_msg("Server Response:\n\n" + self.pretty_json(response) + "\n") @@ -162,19 +159,19 @@ class JsonRpcClient(object): for single_response in response_json: rc, msg = self.process_single_response(single_response) - rc_list.append( CmdResponse(rc, msg) ) + rc_list.append( (rc, msg) ) - return CmdResponse(True, rc_list) + return True, rc_list else: rc, msg = self.process_single_response(response_json) - return CmdResponse(rc, msg) + return rc, msg def process_single_response (self, response_json): if (response_json.get("jsonrpc") != "2.0"): - return False, "Malformed Response ({0})".format(str(response)) + return False, "Malfromed Response ({0})".format(str(response)) # error reported by server if ("error" in response_json): @@ -185,7 +182,7 @@ class JsonRpcClient(object): # if no error there should be a result if ("result" not in response_json): - return False, "Malformed Response ({0})".format(str(response)) + return False, "Malfromed Response ({0})".format(str(response)) return True, response_json["result"] @@ -194,7 +191,7 @@ class JsonRpcClient(object): def set_verbose(self, mode): self.verbose = mode - def disconnect(self): + def disconnect (self): if self.connected: self.socket.close(linger = 0) self.context.destroy(linger = 0) @@ -247,3 +244,270 @@ class JsonRpcClient(object): print "Shutting down RPC client\n" if hasattr(self, "context"): self.context.destroy(linger=0) + +# MOVE THIS TO DAN'S FILE +class TrexStatelessClient(JsonRpcClient): + + def __init__ (self, server, port, user): + + super(TrexStatelessClient, self).__init__(server, port) + + self.user = user + self.port_handlers = {} + + self.supported_cmds = [] + self.system_info = None + self.server_version = None + + + def whoami (self): + return self.user + + def ping_rpc_server(self): + + return self.invoke_rpc_method("ping", block = False) + + def get_rpc_server_version (self): + return self.server_version + + def get_system_info (self): + if not self.system_info: + return {} + + return self.system_info + + def get_supported_cmds(self): + if not self.supported_cmds: + return {} + + return self.supported_cmds + + def get_port_count (self): + if not self.system_info: + return 0 + + return self.system_info["port_count"] + + # sync the client with all the server required data + def sync (self): + + # get server version + rc, msg = self.invoke_rpc_method("get_version") + if not rc: + self.disconnect() + return rc, msg + + self.server_version = msg + + # get supported commands + rc, msg = self.invoke_rpc_method("get_supported_cmds") + if not rc: + self.disconnect() + return rc, msg + + self.supported_cmds = [str(x) for x in msg if x] + + # get system info + rc, msg = self.invoke_rpc_method("get_system_info") + if not rc: + self.disconnect() + return rc, msg + + self.system_info = msg + + return True, "" + + def connect (self): + rc, err = super(TrexStatelessClient, self).connect() + if not rc: + return rc, err + + return self.sync() + + + # take ownership over ports + def take_ownership (self, port_id_array, force = False): + if not self.connected: + return False, "Not connected to server" + + batch = self.create_batch() + + for port_id in port_id_array: + batch.add("acquire", params = {"port_id":port_id, "user":self.user, "force":force}) + + rc, resp_list = batch.invoke() + if not rc: + return rc, resp_list + + for i, rc in enumerate(resp_list): + if rc[0]: + self.port_handlers[port_id_array[i]] = rc[1] + + return True, resp_list + + + def release_ports (self, port_id_array): + batch = self.create_batch() + + for port_id in port_id_array: + + # let the server handle un-acquired errors + if self.port_handlers.get(port_id): + handler = self.port_handlers[port_id] + else: + handler = "" + + batch.add("release", params = {"port_id":port_id, "handler":handler}) + + + rc, resp_list = batch.invoke() + if not rc: + return rc, resp_list + + for i, rc in enumerate(resp_list): + if rc[0]: + self.port_handlers.pop(port_id_array[i]) + + return True, resp_list + + def get_owned_ports (self): + return self.port_handlers.keys() + + # fetch port stats + def get_port_stats (self, port_id_array): + if not self.connected: + return False, "Not connected to server" + + batch = self.create_batch() + + # empty list means all + if port_id_array == []: + port_id_array = list([x for x in xrange(0, self.system_info["port_count"])]) + + for port_id in port_id_array: + + # let the server handle un-acquired errors + if self.port_handlers.get(port_id): + handler = self.port_handlers[port_id] + else: + handler = "" + + batch.add("get_port_stats", params = {"port_id":port_id, "handler":handler}) + + + rc, resp_list = batch.invoke() + + return rc, resp_list + + # snapshot will take a snapshot of all your owned ports for streams and etc. + def snapshot(self): + + + if len(self.get_owned_ports()) == 0: + return {} + + snap = {} + + batch = self.create_batch() + + for port_id in self.get_owned_ports(): + + batch.add("get_port_stats", params = {"port_id": port_id, "handler": self.port_handlers[port_id]}) + batch.add("get_stream_list", params = {"port_id": port_id, "handler": self.port_handlers[port_id]}) + + rc, resp_list = batch.invoke() + if not rc: + return rc, resp_list + + # split the list to 2s + index = 0 + for port_id in self.get_owned_ports(): + if not resp_list[index] or not resp_list[index + 1]: + snap[port_id] = None + continue + + # fetch the first two + stats = resp_list[index][1] + stream_list = resp_list[index + 1][1] + + port = {} + port['status'] = stats['status'] + port['stream_list'] = [] + + # get all the streams + if len(stream_list) > 0: + batch = self.create_batch() + for stream_id in stream_list: + batch.add("get_stream", params = {"port_id": port_id, "stream_id": stream_id, "handler": self.port_handlers[port_id]}) + + rc, stream_resp_list = batch.invoke() + if not rc: + port = {} + + port['streams'] = {} + for i, resp in enumerate(stream_resp_list): + if resp[0]: + port['streams'][stream_list[i]] = resp[1] + + snap[port_id] = port + + # move to next one + index += 2 + + + return snap + + # add stream + # def add_stream (self, port_id, stream_id, isg, next_stream_id, packet, vm=[]): + # if not port_id in self.get_owned_ports(): + # return False, "Port {0} is not owned... please take ownership before adding streams".format(port_id) + # + # handler = self.port_handlers[port_id] + # + # stream = {} + # stream['enabled'] = True + # stream['self_start'] = True + # stream['isg'] = isg + # stream['next_stream_id'] = next_stream_id + # stream['packet'] = {} + # stream['packet']['binary'] = packet + # stream['packet']['meta'] = "" + # stream['vm'] = vm + # stream['rx_stats'] = {} + # stream['rx_stats']['enabled'] = False + # + # stream['mode'] = {} + # stream['mode']['type'] = 'continuous' + # stream['mode']['pps'] = 10.0 + # + # params = {} + # params['handler'] = handler + # params['stream'] = stream + # params['port_id'] = port_id + # params['stream_id'] = stream_id + # + # print params + # return self.invoke_rpc_method('add_stream', params = params) + + def add_stream(self, port_id_array, stream_pack_list): + batch = self.create_batch() + + for port_id in port_id_array: + for stream_pack in stream_pack_list: + params = {"port_id": port_id, + "handler": self.port_handlers[port_id], + "stream_id": stream_pack.stream_id, + "stream": stream_pack.stream} + batch.add("add_stream", params=params) + rc, resp_list = batch.invoke() + if not rc: + return rc, resp_list + + for i, rc in enumerate(resp_list): + if rc[0]: + print "Stream {0} - {1}".format(i, rc[1]) + # self.port_handlers[port_id_array[i]] = rc[1] + + return True, resp_list + + # return self.invoke_rpc_method('add_stream', params = params) diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py index c687126b..3aeb6a34 100755 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -33,6 +33,7 @@ class CTRexPktBuilder(object): self._max_pkt_size = max_pkt_size self.payload_gen = CTRexPktBuilder.CTRexPayloadGen(self._packet, self._max_pkt_size) self.vm = CTRexPktBuilder.CTRexVM() + self.metadata = "" def add_pkt_layer(self, layer_name, pkt_layer): """ @@ -441,8 +442,9 @@ class CTRexPktBuilder(object): if self._packet is None: raise CTRexPktBuilder.EmptyPacketError() pkt_in_hex = binascii.hexlify(str(self._packet)) - return [int(pkt_in_hex[i:i+2], 16) - for i in range(0, len(pkt_in_hex), 2)] + return {"binary": [int(pkt_in_hex[i:i+2], 16) + for i in range(0, len(pkt_in_hex), 2)], + "meta": self.metadata} # return [pkt_in_hex[i:i+2] for i in range(0, len(pkt_in_hex), 2)] def dump_pkt_to_pcap(self, file_path, ts=None): @@ -887,7 +889,7 @@ class CTRexPktBuilder(object): dictionary holds variable data of VM variable """ - return {"ins_name": "flow_var", # VM variable dump always refers to manipulate instruction. + return {"type": "flow_var", # VM variable dump always refers to manipulate instruction. "name": self.name, "size": self.size, "op": self.operation, diff --git a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py new file mode 100755 index 00000000..60630a04 --- /dev/null +++ b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py @@ -0,0 +1,163 @@ + +""" +Dan Klein +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. +""" + +import external_packages +import yaml + + +class CTRexYAMLLoader(object): + TYPE_DICT = {"double":float, + "int":int, + "array":list, + "string":str, + "boolean":bool} + + def __init__(self, yaml_ref_file_path): + self.yaml_path = yaml_ref_file_path + self.ref_obj = None + + def check_term_param_type(self, val, val_field, ref_val, multiplier): + # print val, val_field, ref_val + tmp_type = ref_val.get('type') + if isinstance(tmp_type, list): + # item can be one of multiple types + # print "multiple choice!" + python_types = set() + for t in tmp_type: + if t in self.TYPE_DICT: + python_types.add(self.TYPE_DICT.get(t)) + else: + return False, TypeError("Unknown resolving for type {0}".format(t)) + # print "python legit types: ", python_types + if type(val) not in python_types: + return False, TypeError("Type of object field '{0}' is not allowed".format(val_field)) + else: + # WE'RE OK! + return True, CTRexYAMLLoader._calc_final_value(val, multiplier, ref_val.get('multiply', False)) + else: + # this is a single type field + python_type = self.TYPE_DICT.get(tmp_type) + if not isinstance(val, python_type): + return False, TypeError("Type of object field '{0}' is not allowed".format(val_field)) + else: + # WE'RE OK! + return True, CTRexYAMLLoader._calc_final_value(val, multiplier, ref_val.get('multiply', False)) + + def get_reference_default(self, root_obj, sub_obj, key): + # print root_obj, sub_obj, key + if sub_obj: + ref_field = self.ref_obj.get(root_obj).get(sub_obj).get(key) + else: + ref_field = self.ref_obj.get(root_obj).get(key) + if 'has_default' in ref_field: + if ref_field.get('has_default'): + # WE'RE OK! + return True, ref_field.get('default') + else: + # This is a mandatory field! + return False, ValueError("The {0} field is mandatory and must be specified explicitly".format(key)) + else: + return False, ValueError("The {0} field has no indication about default value".format(key)) + + def validate_yaml(self, evaluated_obj, root_obj, fill_defaults=True, multiplier=1): + if isinstance(evaluated_obj, dict) and evaluated_obj.keys() == [root_obj]: + evaluated_obj = evaluated_obj.get(root_obj) + if not self.ref_obj: + self.ref_obj = load_yaml_to_obj(self.yaml_path) + # self.load_reference() + ref_item = self.ref_obj.get(root_obj) + if ref_item is not None: + try: + typed_obj = [False, None] # first item stores validity (multiple object "shapes"), second stored type + if "type" in evaluated_obj: + ref_item = ref_item[evaluated_obj.get("type")] + # print "lower resolution with typed object" + typed_obj = [True, evaluated_obj.get("type")] + if isinstance(ref_item, dict) and "type" not in ref_item: # this is not a terminal + result_obj = {} + if typed_obj[0]: + result_obj["type"] = typed_obj[1] + # print "processing dictionary non-terminal value" + for k, v in ref_item.items(): + # print "processing element '{0}' with value '{1}'".format(k,v) + if k in evaluated_obj: + # validate with ref obj + # print "found in evaluated object!" + tmp_type = v.get('type') + # print tmp_type + # print evaluated_obj + if tmp_type == "object": + # go deeper into nesting hierarchy + # print "This is an object type, recursion!" + result_obj[k] = self.validate_yaml(evaluated_obj.get(k), k, fill_defaults, multiplier) + else: + # validation on terminal type + # print "Validating terminal type %s" % k + res_ok, data = self.check_term_param_type(evaluated_obj.get(k), k, v, multiplier) + if res_ok: + # data field contains the value to save + result_obj[k] = data + else: + # data var contains the exception to throw + raise data + elif fill_defaults: + # complete missing values with default value, if exists + sub_obj = typed_obj[1] if typed_obj[0] else None + res_ok, data = self.get_reference_default(root_obj, sub_obj, k) + if res_ok: + # data field contains the value to save + result_obj[k] = data + else: + # data var contains the exception to throw + raise data + return result_obj + elif isinstance(ref_item, list): + # currently not handling list objects + return NotImplementedError("List object are currently unsupported") + else: + raise TypeError("Unknown parse tree object type.") + except KeyError as e: + raise + else: + raise KeyError("The given root_key '{key}' does not exists on reference object".format(key=root_obj)) + + @staticmethod + def _calc_final_value(val, multiplier, multiply): + def to_num(s): + try: + return int(s) + except ValueError: + return float(s) + if multiply: + return val * to_num(multiplier) + else: + return val + + +def load_yaml_to_obj(file_path): + try: + return yaml.load(file(file_path, 'r')) + except yaml.YAMLError as e: + raise + except Exception as e: + raise + +def yaml_exporter(file_path): + pass + +if __name__ == "__main__": + pass diff --git a/scripts/automation/trex_control_plane/common/external_packages.py b/scripts/automation/trex_control_plane/common/external_packages.py new file mode 100755 index 00000000..62121d4f --- /dev/null +++ b/scripts/automation/trex_control_plane/common/external_packages.py @@ -0,0 +1,28 @@ +#!/router/bin/python + +import sys +import os + +CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) +ROOT_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir)) # path to trex_control_plane directory +PATH_TO_PYTHON_LIB = os.path.abspath(os.path.join(ROOT_PATH, os.pardir, os.pardir, 'external_libs')) + +CLIENT_UTILS_MODULES = ['PyYAML-3.01/lib' + ] + +def import_common_modules(): + # must be in a higher priority + sys.path.insert(0, PATH_TO_PYTHON_LIB) + sys.path.append(ROOT_PATH) + import_module_list(CLIENT_UTILS_MODULES) + + +def import_module_list(modules_list): + assert(isinstance(modules_list, list)) + for p in modules_list: + full_path = os.path.join(PATH_TO_PYTHON_LIB, p) + fix_path = os.path.normcase(full_path) + sys.path.insert(1, full_path) + +import_common_modules() + diff --git a/scripts/automation/trex_control_plane/common/rpc_defaults.yaml b/scripts/automation/trex_control_plane/common/rpc_defaults.yaml new file mode 100755 index 00000000..32631609 --- /dev/null +++ b/scripts/automation/trex_control_plane/common/rpc_defaults.yaml @@ -0,0 +1,115 @@ +##############################################################
+#### TRex RPC stream list default values ####
+##############################################################
+
+# this document is based on TRex RPC server spec and its fields:
+# http://trex-tgn.cisco.com/trex/doc/trex_rpc_server_spec.html
+
+### HOW TO READ THIS FILE
+# 1. Each key represents an object type
+# 2. Each value can be either a value field or another object
+# 2.1. If a value field, read as:
+# + type: type of field
+# + has_default: if the value has any default
+# + default: the default value (Only appears if has_default field is 'YES')
+# 2.2. If an object type, jump to corresponding object key.
+# 3. If an object has more than one instance type, another layer with the type shall be added.
+# For example, 'mode' object has 3 types: 'continuous', 'single_burst', 'multi_burst'
+# So, 3 mode objects will be defined, named:
+# - mode['continuous']
+# - mode['single_burst']
+# - mode['multi_burst']
+# In this case, there's no default for the 'type' field on the object
+# 4. Some values has 'multiply' property attached.
+# In such case, the loaded value will be multiplied by the multiplier
+# For example, if the mode's 'pps' field value is 10, and its multiplier is 5,
+# the loaded pps value will be 10*5=50
+# 5. Any object type must be listed by the user, even if all its field are defaults.
+# The most basic option would be to declare the object with "[]", which stands for empty object in YAML syntax.
+
+
+stream:
+ enabled:
+ type: boolean
+ has_default: YES
+ default: True
+ self_start:
+ type: boolean
+ has_default: YES
+ default: True
+ isg:
+ type: [int, double, string]
+ has_default: YES
+ default: 0.0
+ next_stream_id:
+ type: string # string to allow naming binding
+ has_default: YES
+ default: -1 # no next streams
+ packet:
+ type: object
+ mode:
+ type: object
+ vm:
+ type: array
+ has_default: YES
+ default: [] # no ranging instructions
+ rx_stats:
+ type: object
+
+packet:
+ binary:
+ type: [array,string]
+ has_default: NO
+ meta:
+ type: string
+ has_default: YES
+ default: ""
+
+mode:
+ continuous:
+ pps:
+ type: [int, double]
+ has_default: NO
+ multiply: YES
+ single_burst:
+ pps:
+ type: [int, double]
+ has_default: NO
+ multiply: YES
+ total_pkts:
+ type: int
+ has_default: NO
+ multi_burst:
+ pps:
+ type: [int, double]
+ has_default: NO
+ multiply: YES
+ pkts_per_burst:
+ type: int
+ has_default: NO
+ ibg:
+ type: [int, double, string]
+ has_default: YES
+ default: 100.0
+ count:
+ type: int
+ has_default: YES
+ default: 0 # loop forever
+
+rx_stats:
+ enabled:
+ type: boolean
+ has_default: YES
+ default: False
+ stream_id:
+ type: string
+ has_default: YES
+ default: False # use related stream_id
+ seq_enabled:
+ type: boolean
+ has_default: YES
+ default: False
+ latency_enabled:
+ type: boolean
+ has_default: YES
+ default: False
\ No newline at end of file diff --git a/scripts/automation/trex_control_plane/common/trex_status.py b/scripts/automation/trex_control_plane/common/trex_status.py new file mode 100644 index 00000000..f132720c --- /dev/null +++ b/scripts/automation/trex_control_plane/common/trex_status.py @@ -0,0 +1,8 @@ +#!/router/bin/python + +# define the states in which a T-Rex can hold during its lifetime +# TRexStatus = Enum('TRexStatus', 'Idle Starting Running') + +IDLE = 1 +STARTING = 2 +RUNNING = 3 diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py new file mode 100755 index 00000000..783f2769 --- /dev/null +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -0,0 +1,248 @@ +#!/router/bin/python + +import external_packages +from client_utils.packet_builder import CTRexPktBuilder +from collections import OrderedDict, namedtuple +from client_utils.yaml_utils import * +import dpkt +import struct +import copy +import os + +StreamPack = namedtuple('StreamPack', ['stream_id', 'stream']) + +class CStreamList(object): + + def __init__(self): + self.streams_list = {} + self.yaml_loader = CTRexYAMLLoader(os.path.join(os.path.dirname(os.path.realpath(__file__)), + "rpc_defaults.yaml")) + + def append_stream(self, name, stream_obj): + assert isinstance(stream_obj, CStream) + if name in self.streams_list: + raise NameError("A stream with this name already exists on this list.") + self.streams_list[name]=stream_obj + return + + def remove_stream(self, name): + popped = self.streams_list.pop(name) + if popped: + for stream_name, stream in self.streams_list.items(): + if stream.next_stream_id == name: + stream.next_stream_id = -1 + try: + rx_stats_stream = getattr(stream.rx_stats, "stream_id") + if rx_stats_stream == name: + # if a referenced stream of rx_stats object deleted, revert to rx stats of current stream + setattr(stream.rx_stats, "stream_id", stream_name) + except AttributeError as e: + continue # + return popped + + def export_to_yaml(self, file_path): + raise NotImplementedError("export_to_yaml method is not implemented, yet") + + def load_yaml(self, file_path, multiplier=1): + # clear all existing streams linked to this object + self.streams_list.clear() + streams_data = load_yaml_to_obj(file_path) + assert isinstance(streams_data, list) + for stream in streams_data: + stream_name = stream.get("name") + raw_stream = stream.get("stream") + if not stream_name or not raw_stream: + raise ValueError("Provided stream is not according to convention." + "Each stream must be provided as two keys: 'name' and 'stream'. " + "Provided item was:\n {stream}".format(stream)) + new_stream_data = self.yaml_loader.validate_yaml(raw_stream, + "stream", + multiplier= multiplier) + new_stream_obj = CStream() + new_stream_obj.load_data(**new_stream_data) + self.append_stream(stream_name, new_stream_obj) + return new_stream_data + + def compile_streams(self): + # first, assign an id to each stream + stream_ids = {} + for idx, stream_name in enumerate(self.streams_list): + stream_ids[stream_name] = idx + # next, iterate over the streams and transform them from working with names to ids. + # with that build a new dict with old stream_name as the key, and StreamPack as the stored value + compiled_streams = {} + for stream_name, stream in self.streams_list.items(): + tmp_stream = CStreamList._compile_single_stream(stream_name, stream, stream_ids) + compiled_streams[stream_name] = StreamPack(stream_ids.get(stream_name), + tmp_stream) + return compiled_streams + + @staticmethod + def _compile_single_stream(stream_name, stream, id_dict): + # copy the old stream to temporary one, no change to class attributes + tmp_stream = copy.copy(stream) + next_stream_id = id_dict.get(getattr(tmp_stream, "next_stream_id"), -1) + try: + rx_stats_stream_id = id_dict.get(getattr(tmp_stream.rx_stats, "stream_id"), + id_dict.get(stream_name)) + except AttributeError as e: + rx_stats_stream_id = id_dict.get(stream_name) + # assign resolved values to stream object + tmp_stream.next_stream_id = next_stream_id + tmp_stream.rx_stats.stream_id = rx_stats_stream_id + return tmp_stream + + +class CRxStats(object): + + FIELDS = ["seq_enabled", "latency_enabled", "stream_id"] + def __init__(self, enabled=False, **kwargs): + self.enabled = bool(enabled) + for field in CRxStats.FIELDS: + setattr(self, field, kwargs.get(field, False)) + + def dump(self): + if self.enabled: + dump = {"enabled": True} + dump.update({k: getattr(self, k) + for k in CRxStats.FIELDS} + ) + return dump + else: + return {"enabled": False} + + + +class CTxMode(object): + """docstring for CTxMode""" + GENERAL_FIELDS = ["type", "pps"] + FIELDS = {"continuous": [], + "single_burst": ["total_pkts"], + "multi_burst": ["pkts_per_burst", "ibg", "count"]} + + def __init__(self, type, pps=0, **kwargs): + self._MODES = CTxMode.FIELDS.keys() + self.type = type + self.pps = pps + for field in CTxMode.FIELDS.get(self.type): + setattr(self, field, kwargs.get(field, 0)) + + @property + def type(self): + return self._type + + @type.setter + def type(self, type): + if type not in self._MODES: + raise ValueError("Unknown TX mode ('{0}')has been initialized.".format(type)) + self._type = type + self._reset_fields() + + def dump(self): + dump = ({k: getattr(self, k) + for k in CTxMode.GENERAL_FIELDS + }) + dump.update({k: getattr(self, k) + for k in CTxMode.FIELDS.get(self.type) + }) + return dump + + def _reset_fields(self): + for field in CTxMode.FIELDS.get(self.type): + setattr(self, field, 0) + + +class CStream(object): + """docstring for CStream""" + + FIELDS = ["enabled", "self_start", "next_stream_id", "isg", "mode", "rx_stats", "packet", "vm"] + # COMPILE_FIELDS = ["enabled", "self_start", "next_stream_id", "isg", "mode", "rx_stats", "packet", "vm"] + + def __init__(self): + self.is_loaded = False + self._is_compiled = False + for field in CStream.FIELDS: + setattr(self, field, None) + + def load_data(self, **kwargs): + try: + for k in CStream.FIELDS: + if k == "rx_stats": + rx_stats_data = kwargs[k] + if isinstance(rx_stats_data, dict): + setattr(self, k, CRxStats(**rx_stats_data)) + elif isinstance(rx_stats_data, CRxStats): + setattr(self, k, rx_stats_data) + elif k == "mode": + tx_mode = kwargs[k] + if isinstance(tx_mode, dict): + setattr(self, k, CTxMode(**tx_mode)) + elif isinstance(tx_mode, CTxMode): + setattr(self, k, tx_mode) + elif k == "packet": + if isinstance(kwargs[k], CTRexPktBuilder): + if "vm" not in kwargs: + self.load_packet_obj(kwargs[k]) + else: + raise ValueError("When providing packet object with a CTRexPktBuilder, vm parameter " + "should not be supplied") + else: + binary = kwargs[k]["binary"] + if isinstance(binary, list): + setattr(self, k, kwargs[k]) + elif isinstance(binary, str) and binary.endswith(".pcap"): + self.load_packet_from_pcap(binary, kwargs[k]["meta"]) + else: + raise ValueError("Packet binary attribute has been loaded with unsupported value." + "Supported values are reference to pcap file with SINGLE packet, " + "or a list of unsigned-byte integers") + else: + setattr(self, k, kwargs[k]) + self.is_loaded = True + except KeyError as e: + cause = e.args[0] + raise KeyError("The attribute '{0}' is missing as a field of the CStream object.\n" + "Loaded data must contain all of the following fields: {1}".format(cause, CStream.FIELDS)) + + def load_packet_obj(self, packet_obj): + assert isinstance(packet_obj, CTRexPktBuilder) + self.packet = packet_obj.dump_pkt() + self.vm = packet_obj.get_vm_data() + + def load_packet_from_pcap(self, pcap_path, metadata=''): + with open(pcap_path, 'r') as f: + pcap = dpkt.pcap.Reader(f) + first_packet = True + for _, buf in pcap: + # this is an iterator, can't evaluate the number of files in advance + if first_packet: + self.packet = {"binary": [struct.unpack('B', buf[i:i+1])[0] # represent data as list of 0-255 ints + for i in range(0, len(buf))], + "meta": metadata} # meta data continues without a change. + first_packet = False + else: + raise ValueError("Provided pcap file contains more than single packet.") + # arrive here ONLY if pcap contained SINGLE packet + return + + + def dump(self, compilation=False): + # fields = CStream.COMPILE_FIELDS if compilation else CStream.FIELDS + if self.is_loaded: + dump = {} + for key in CStream.FIELDS: + try: + dump[key] = getattr(self, key).dump() # use dump() method of compound object, such TxMode + except AttributeError: + dump[key] = getattr(self, key) + return dump + else: + raise RuntimeError("CStream object isn't loaded with data. Use 'load_data' method.") + + def dump_compiled(self): + return self.dump(compilation=True) + + + +if __name__ == "__main__": + pass diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 3aeab901..a9ac040b 100644..100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -1,18 +1,124 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- + +""" +Dan Klein, 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. +""" + import cmd import json import ast import argparse import random import string - +import os import sys +import tty, termios import trex_root_path +from common.trex_streams import * + from client_utils.jsonrpc_client import TrexStatelessClient import trex_status +from collections import namedtuple + +LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) + +# + +def readch (choices = []): + + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + while True: + ch = sys.stdin.read(1) + if (ord(ch) == 3) or (ord(ch) == 4): + return None + if ch in choices: + return ch + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + + return None + +class YesNoMenu(object): + def __init__ (self, caption): + self.caption = caption + + def show (self): + print "{0}".format(self.caption) + sys.stdout.write("[Y/y/N/n] : ") + ch = readch(choices = ['y', 'Y', 'n', 'N']) + if ch == None: + return None + + print "\n" + if ch == 'y' or ch == 'Y': + return True + else: + return False + +# multi level cmd menu +class CmdMenu(object): + def __init__ (self): + self.menus = [] + + + def add_menu (self, caption, options): + menu = {} + menu['caption'] = caption + menu['options'] = options + self.menus.append(menu) + + def show (self): + cur_level = 0 + print "\n" + + selected_path = [] + for menu in self.menus: + # show all the options + print "{0}\n".format(menu['caption']) + for i, option in enumerate(menu['options']): + print "{0}. {1}".format(i + 1, option) + + #print "\nPlease select an option: " + choices = range(0, len(menu['options'])) + choices = [ chr(x + 48) for x in choices] + + print "" + ch = readch(choices) + print "" + + if ch == None: + return None + + selected_path.append(int(ch) - 1) + + return selected_path + + +class AddStreamMenu(CmdMenu): + def __init__ (self): + super(AddStreamMenu, self).__init__() + self.add_menu('Please select type of stream', ['a', 'b', 'c']) + self.add_menu('Please select ISG', ['d', 'e', 'f']) + +# main console object class TrexConsole(cmd.Cmd): """Trex Console""" @@ -29,6 +135,8 @@ class TrexConsole(cmd.Cmd): self.verbose = False self.postcmd(False, "") + + self.user_streams = {} # a cool hack - i stole this function and added space @@ -108,6 +216,13 @@ class TrexConsole(cmd.Cmd): def do_acquire (self, line, force = False): '''Acquire ports\n''' + # make sure that the user wants to acquire all + if line == "": + ask = YesNoMenu('Do you want to acquire all ports ? ') + rc = ask.show() + if rc == False: + return + port_list = self.parse_ports_from_line(line) if not port_list: return @@ -312,25 +427,142 @@ class TrexConsole(cmd.Cmd): print "{:<30} {:<30}".format(cmd + " - ", help) + def do_load_stream_list(self, line): + '''Loads a YAML stream list serialization into user console \n''' + args = line.split() + if args >= 2: + name = args[0] + yaml_path = args[1] + try: + multiplier = args[2] + except IndexError: + multiplier = 1 + stream_list = CStreamList() + loaded_obj = stream_list.load_yaml(yaml_path, multiplier) + # print self.rpc_client.pretty_json(json.dumps(loaded_obj)) + if name in self.user_streams: + print "Picked name already exist. Please pick another name." + else: + try: + compiled_streams = stream_list.compile_streams() + self.user_streams[name] = LoadedStreamList(loaded_obj, + [StreamPack(v.stream_id, v.stream.dump_compiled()) + for k, v in compiled_streams.items()]) + + print "Stream '{0}' loaded successfully".format(name) + except Exception as e: + raise + return + else: + print "please provide load name and YAML path, separated by space.\n" \ + "Optionally, you may provide a third argument to specify multiplier." + + @staticmethod + def tree_autocomplete(text): + dir = os.path.dirname(text) + if dir: + path = dir + else: + path = "." + start_string = os.path.basename(text) + return [x + for x in os.listdir(path) + if x.startswith(start_string)] + + + def complete_load_stream_list(self, text, line, begidx, endidx): + arg_num = len(line.split()) - 1 + if arg_num == 2: + return TrexConsole.tree_autocomplete(line.split()[-1]) + else: + return [text] + + def do_show_stream_list(self, line): + '''Shows the loaded stream list named [name] \n''' + args = line.split() + if args: + list_name = args[0] + try: + stream = self.user_streams[list_name] + if len(args) >= 2 and args[1] == "full": + print self.rpc_client.pretty_json(json.dumps(stream.compiled)) + else: + print self.rpc_client.pretty_json(json.dumps(stream.loaded)) + except KeyError as e: + print "Unknown stream list name provided" + else: + print "\nAvailable stream lists:\n{0}".format(', '.join([x + for x in self.user_streams.keys()])) + + def complete_show_stream_list(self, text, line, begidx, endidx): + return [x + for x in self.user_streams.keys() + if x.startswith(text)] + + def do_attach(self, line): + args = line.split() + if len(args) >= 1: + try: + stream_list = self.user_streams[args[0]] + port_list = self.parse_ports_from_line(' '.join(args[1:])) + owned = set(self.rpc_client.get_owned_ports()) + if set(port_list).issubset(owned): + rc, resp_list = self.rpc_client.add_stream(port_list, stream_list.compiled) + if not rc: + print "\n*** " + resp_list + "\n" + return + else: + print "Not all desired ports are aquired.\n" \ + "Acquired ports are: {acq}\n" \ + "Requested ports: {req}\n" \ + "Missing ports: {miss}".format(acq=list(owned), + req=port_list, + miss=list(set(port_list).difference(owned))) + except KeyError as e: + cause = e.args[0] + print "Provided stream list name '{0}' doesn't exists.".format(cause) + else: + print "Please provide list name and ports to attach to, or leave empty to attach to all ports." + - # do - #def do_snapshot (self, line): - #for key, value in self.rpc_client.snapshot()[1]['streams'].iteritems(): - #print str(key) + " " + str(value) + + + + + + + # adds a very simple stream + def do_add_simple_stream (self, line): + if line == "": + add_stream = AddStreamMenu() + add_stream.show() + return + + params = line.split() + port_id = int(params[0]) + stream_id = int(params[1]) + + packet = [0xFF,0xFF,0xFF] + rc, msg = self.rpc_client.add_stream(port_id = port_id, stream_id = stream_id, isg = 1.1, next_stream_id = -1, packet = packet) + if rc: + print "\nServer Response:\n\n" + self.rpc_client.pretty_json(json.dumps(msg)) + "\n" + else: + print "\n*** " + msg + "\n" + # aliasing do_exit = do_EOF = do_q = do_quit def setParserOptions (): parser = argparse.ArgumentParser(prog="trex_console.py") - parser.add_argument("-s", "--server", help = "T-Rex Server [default is localhost]", + parser.add_argument("-s", "--server", help = "TRex Server [default is localhost]", default = "localhost", type = str) - parser.add_argument("-p", "--port", help = "T-Rex Server Port [default is 5050]\n", + parser.add_argument("-p", "--port", help = "TRex Server Port [default is 5050]\n", default = 5050, type = int) diff --git a/scripts/automation/trex_control_plane/console/trex_root_path.py b/scripts/automation/trex_control_plane/console/trex_root_path.py index de4ec03b..de4ec03b 100644..100755 --- a/scripts/automation/trex_control_plane/console/trex_root_path.py +++ b/scripts/automation/trex_control_plane/console/trex_root_path.py diff --git a/scripts/automation/trex_control_plane/console/trex_status.py b/scripts/automation/trex_control_plane/console/trex_status.py index b881f9f5..2c5a648f 100644 --- a/scripts/automation/trex_control_plane/console/trex_status.py +++ b/scripts/automation/trex_control_plane/console/trex_status.py @@ -170,7 +170,7 @@ class PortsStatsPanel(TrexStatusPanel): port_stats = self.status_obj.stats.get_port_stats(port_index) if port_stats: - self.getwin().addstr(5 + (i * 4), 2, "{:^15} {:^15,} {:^15,} {:^15,} {:^15,} {:^15,} {:^15,}".format( + self.getwin().addstr(5 + (i * 4), 2, "{:^15} {:^15,.2f} {:^15,.2f} {:^15,} {:^15,.2f} {:^15,.2f} {:^15,}".format( "{0} ({1})".format(str(port_index), self.status_obj.server_sys_info["ports"][port_index]["speed"]), port_stats["tx_pps"], port_stats["tx_bps"], diff --git a/scripts/automation/trex_control_plane/examples/client_interactive_example.py b/scripts/automation/trex_control_plane/examples/client_interactive_example.py index 9ee28898..d21b2b15 100755 --- a/scripts/automation/trex_control_plane/examples/client_interactive_example.py +++ b/scripts/automation/trex_control_plane/examples/client_interactive_example.py @@ -74,7 +74,7 @@ class InteractiveTRexClient(cmd.Cmd): print termstyle.green("*** End of TRex status prompt ***") def do_show_trex_files_path (self, line): - """Prompts the local path in which files are stored when pushed to t-rex server from client""" + """Prompts the local path in which files are stored when pushed to trex server from client""" print self.trex.get_trex_files_path() print termstyle.green("*** End of trex_files_path prompt ***") diff --git a/scripts/automation/trex_control_plane/examples/interactive_stateless.py b/scripts/automation/trex_control_plane/examples/interactive_stateless.py index 7c25b4ef..e64b4755 100644 --- a/scripts/automation/trex_control_plane/examples/interactive_stateless.py +++ b/scripts/automation/trex_control_plane/examples/interactive_stateless.py @@ -76,18 +76,18 @@ class InteractiveStatelessTRex(cmd.Cmd): def do_push_files(self, filepaths): - """Pushes a custom file to be stored locally on T-Rex server.\ + """Pushes a custom file to be stored locally on TRex server.\ \nPush multiple files by specifying their path separated by ' ' (space).""" try: filepaths = filepaths.split(' ') - print termstyle.green("*** Starting pushing files ({trex_files}) to T-Rex. ***".format( + print termstyle.green("*** Starting pushing files ({trex_files}) to TRex. ***".format( trex_files=', '.join(filepaths)) ) ret_val = self.trex.push_files(filepaths) if ret_val: - print termstyle.green("*** End of T-Rex push_files method (success) ***") + print termstyle.green("*** End of TRex push_files method (success) ***") else: - print termstyle.magenta("*** End of T-Rex push_files method (failed) ***") + print termstyle.magenta("*** End of TRex push_files method (failed) ***") except IOError as inst: print termstyle.magenta(inst) @@ -99,10 +99,10 @@ if __name__ == "__main__": parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0 \t (C) Cisco Systems Inc.\n') parser.add_argument("-t", "--trex-host", required = True, dest="trex_host", - action="store", help="Specify the hostname or ip to connect with T-Rex server.", + action="store", help="Specify the hostname or ip to connect with TRex server.", metavar="HOST" ) parser.add_argument("-p", "--trex-port", type=int, default = 5050, metavar="PORT", dest="trex_port", - help="Select port on which the T-Rex server listens. Default port is 5050.", action="store") + help="Select port on which the TRex server listens. Default port is 5050.", action="store") # parser.add_argument("-m", "--maxhist", type=int, default = 100, metavar="SIZE", dest="hist_size", # help="Specify maximum history size saved at client side. Default size is 100.", action="store") parser.add_argument("--virtual", dest="virtual", @@ -124,5 +124,5 @@ if __name__ == "__main__": except socket.error, e: if e.errno == errno.ECONNREFUSED: raise socket.error(errno.ECONNREFUSED, - "Connection from T-Rex server was terminated. \ + "Connection from TRex server was terminated. \ Please make sure the server is up.") diff --git a/scripts/automation/trex_control_plane/unit_tests/control_plane_general_test.py b/scripts/automation/trex_control_plane/unit_tests/control_plane_general_test.py index 95f259b8..32ad5243 100755 --- a/scripts/automation/trex_control_plane/unit_tests/control_plane_general_test.py +++ b/scripts/automation/trex_control_plane/unit_tests/control_plane_general_test.py @@ -9,7 +9,7 @@ Name: Description: - This script creates the functionality to test the performance of the T-Rex traffic generator control plane. + This script creates the functionality to test the performance of the TRex traffic generator control plane. The scenarios assumes a WORKING server is listening and processing the requests. :: diff --git a/scripts/automation/trex_control_plane/unit_tests/control_plane_unit_test.py b/scripts/automation/trex_control_plane/unit_tests/control_plane_unit_test.py index 37130ee4..1120256c 100755 --- a/scripts/automation/trex_control_plane/unit_tests/control_plane_unit_test.py +++ b/scripts/automation/trex_control_plane/unit_tests/control_plane_unit_test.py @@ -18,7 +18,7 @@ class TRexCPConfiguringPlugin(Plugin): super(TRexCPConfiguringPlugin, self).options(parser, env) parser.add_option('-t', '--trex-server', action='store', dest='trex_server', default='trex-dan', - help='Specify T-Rex server hostname. This server will be used to test control-plane functionality.') + help='Specify TRex server hostname. This server will be used to test control-plane functionality.') def configure(self, options, conf): if options.trex_server: diff --git a/scripts/automation/trex_control_plane/unit_tests/functional_test.py b/scripts/automation/trex_control_plane/unit_tests/functional_test.py index f742403d..30836985 100755 --- a/scripts/automation/trex_control_plane/unit_tests/functional_test.py +++ b/scripts/automation/trex_control_plane/unit_tests/functional_test.py @@ -37,7 +37,7 @@ class CTRexStartStop_Test(CControlPlaneGeneral_Test): def test_parameter_name_error(self): ret = self.trex.start_trex( c = 4, - wrong_key = 1.1, # <----- This key does not exists in T-Rex API + wrong_key = 1.1, # <----- This key does not exists in TRex API d = 70, f = 'avl/sfr_delay_10_1g.yaml', nc = True, @@ -50,7 +50,7 @@ class CTRexStartStop_Test(CControlPlaneGeneral_Test): run_status = self.trex.get_running_status() assert isinstance(run_status, dict) assert_equal (run_status['state'], TRexStatus.Idle ) - assert_equal (run_status['verbose'], "T-Rex run failed due to wrong input parameters, or due to reachability issues.") + assert_equal (run_status['verbose'], "TRex run failed due to wrong input parameters, or due to reachability issues.") assert_raises(TRexError, self.trex.get_running_info) def test_too_early_sample(self): @@ -83,33 +83,33 @@ class CTRexStartStop_Test(CControlPlaneGeneral_Test): assert self.trex.is_running() == False def test_start_more_than_once_same_user(self): - assert self.trex.is_running() == False # first, make sure T-Rex is not running - ret = self.trex.start_trex(**self.valid_start_params) # start 1st T-Rex run + assert self.trex.is_running() == False # first, make sure TRex is not running + ret = self.trex.start_trex(**self.valid_start_params) # start 1st TRex run assert ret == True # make sure 1st run submitted successfuly # time.sleep(1) - assert_raises(TRexInUseError, self.trex.start_trex, **self.valid_start_params) # try to start T-Rex again + assert_raises(TRexInUseError, self.trex.start_trex, **self.valid_start_params) # try to start TRex again ret = self.trex.stop_trex() assert ret==True # make sure stop succeeded assert self.trex.is_running() == False def test_start_more_than_once_different_users(self): - assert self.trex.is_running() == False # first, make sure T-Rex is not running - ret = self.trex.start_trex(**self.valid_start_params) # start 1st T-Rex run + assert self.trex.is_running() == False # first, make sure TRex is not running + ret = self.trex.start_trex(**self.valid_start_params) # start 1st TRex run assert ret == True # make sure 1st run submitted successfuly # time.sleep(1) tmp_trex = CTRexClient(self.trex_server_name) # initialize another client connecting same server - assert_raises(TRexInUseError, tmp_trex.start_trex, **self.valid_start_params) # try to start T-Rex again + assert_raises(TRexInUseError, tmp_trex.start_trex, **self.valid_start_params) # try to start TRex again ret = self.trex.stop_trex() assert ret==True # make sure stop succeeded assert self.trex.is_running() == False def test_simultaneous_sampling(self): - assert self.trex.is_running() == False # first, make sure T-Rex is not running + assert self.trex.is_running() == False # first, make sure TRex is not running tmp_trex = CTRexClient(self.trex_server_name) # initialize another client connecting same server - ret = self.trex.start_trex(**self.valid_start_params) # start T-Rex run + ret = self.trex.start_trex(**self.valid_start_params) # start TRex run assert ret == True # make sure 1st run submitted successfuly time.sleep(6) @@ -123,7 +123,7 @@ class CTRexStartStop_Test(CControlPlaneGeneral_Test): assert tmp_trex.get_result_obj().is_valid_hist() == True if self.trex.get_result_obj().is_done_warmup(): assert tmp_trex.get_result_obj().is_done_warmup() == True - # except TRexError as inst: # T-Rex might have stopped between is_running result and get_running_info() call + # except TRexError as inst: # TRex might have stopped between is_running result and get_running_info() call # # hence, ingore that case # break @@ -132,7 +132,7 @@ class CTRexStartStop_Test(CControlPlaneGeneral_Test): def test_fast_toggling(self): assert self.trex.is_running() == False for i in range(20): - ret = self.trex.start_trex(**self.valid_start_params) # start T-Rex run + ret = self.trex.start_trex(**self.valid_start_params) # start TRex run assert ret == True assert self.trex.is_running() == False # we expect the status to be 'Starting' ret = self.trex.stop_trex() diff --git a/scripts/automation/trex_perf.py b/scripts/automation/trex_perf.py index 5d11f549..beec5061 100755 --- a/scripts/automation/trex_perf.py +++ b/scripts/automation/trex_perf.py @@ -950,7 +950,7 @@ def generate_job_id (): return (str(int(random.getrandbits(32)))) def print_header (): - logger.log("--== T-Trex Performance Tool v1.0 (2014) ==--") + logger.log("--== TRex Performance Tool v1.0 (2014) ==--") # print startup summary def log_startup_summary (job_summary): @@ -960,10 +960,10 @@ def log_startup_summary (job_summary): logger.log("\nWork Request Details:\n") logger.log("Setup Details:\n") - logger.log("T-Rex Config File: {0}".format(job_summary['config_file'])) + logger.log("TRex Config File: {0}".format(job_summary['config_file'])) logger.log("Machine Name: {0}".format(trex_config['trex_name'])) - logger.log("T-Rex Type: {0}".format(trex_config['trex_machine_type'])) - logger.log("T-Rex Dual Int. Tx: {0}".format(trex_config['trex_is_dual'])) + logger.log("TRex Type: {0}".format(trex_config['trex_machine_type'])) + logger.log("TRex Dual Int. Tx: {0}".format(trex_config['trex_is_dual'])) logger.log("Router Interface: {0}".format(trex_config['router_interface'])) logger.log("\nJob Details:\n") diff --git a/scripts/cfg/cfg_example1.yaml b/scripts/cfg/cfg_example1.yaml index bfd7fd88..224fb15e 100755 --- a/scripts/cfg/cfg_example1.yaml +++ b/scripts/cfg/cfg_example1.yaml @@ -6,8 +6,15 @@ enable_zmq_pub : true # enable publisher for stats data zmq_pub_port : 4507 telnet_port : 4508 # the telnet port in case it is enable ( with intercative mode ) + platform : + master_thread_id : 12 + latency_thread_id : 13 + dual_if : + - socket : 1 + threads : [8,9,10,11] + port_info : # set eh mac addr - - dest_mac : [0x1,0x0,0x0,0x1,0x0,0x00] # port 0 + - dest_mac : [1,0x0,0x0,0x1,0x0,0x00] # port 0 src_mac : [0x2,0x0,0x0,0x2,0x0,0x00] - dest_mac : [0x3,0x0,0x0,0x3,0x0,0x00] # port 1 src_mac : [0x4,0x0,0x0,0x4,0x0,0x00] diff --git a/scripts/stl/imix_1pkt.yaml b/scripts/stl/imix_1pkt.yaml new file mode 100755 index 00000000..511f8695 --- /dev/null +++ b/scripts/stl/imix_1pkt.yaml @@ -0,0 +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
+ rx_stats: []
\ No newline at end of file diff --git a/scripts/stl/imix_2pkt.yaml b/scripts/stl/imix_2pkt.yaml new file mode 100755 index 00000000..17a7bdc1 --- /dev/null +++ b/scripts/stl/imix_2pkt.yaml @@ -0,0 +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
+ rx_stats: []
\ No newline at end of file diff --git a/scripts/stl/imix_3pkt.yaml b/scripts/stl/imix_3pkt.yaml new file mode 100755 index 00000000..d3923fb8 --- /dev/null +++ b/scripts/stl/imix_3pkt.yaml @@ -0,0 +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
+ rx_stats: []
\ No newline at end of file diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp index 7cbeb09d..c3581c55 100755 --- a/src/bp_sim.cpp +++ b/src/bp_sim.cpp @@ -2350,21 +2350,13 @@ void operator >> (const YAML::Node& node, CFlowYamlDpPkt & fi) { void operator >> (const YAML::Node& node, CVlanYamlInfo & fi) { uint32_t tmp; - try { - node["enable"] >> tmp ; - fi.m_enable=tmp; - }catch ( const std::exception& e ) { - - } - - try { - node["vlan0"] >> tmp; - fi.m_vlan_per_port[0] = tmp; - node["vlan1"] >> tmp; - fi.m_vlan_per_port[1] = tmp; - }catch ( const std::exception& e ) { - // there is a default - + if ( node.FindValue("enable") ){ + node["enable"] >> tmp ; + fi.m_enable=tmp; + node["vlan0"] >> tmp; + fi.m_vlan_per_port[0] = tmp; + node["vlan1"] >> tmp; + fi.m_vlan_per_port[1] = tmp; } } @@ -2372,15 +2364,15 @@ void operator >> (const YAML::Node& node, CVlanYamlInfo & fi) { void operator >> (const YAML::Node& node, CFlowYamlInfo & fi) { node["name"] >> fi.m_name; - - try { + + if ( node.FindValue("client_pool") ){ node["client_pool"] >> fi.m_client_pool_name; - } catch ( const std::exception& e ) { + }else{ fi.m_client_pool_name = "default"; } - try { + if ( node.FindValue("server_pool") ){ node["server_pool"] >> fi.m_server_pool_name; - } catch ( const std::exception& e ) { + }else{ fi.m_server_pool_name = "default"; } @@ -2393,37 +2385,38 @@ void operator >> (const YAML::Node& node, CFlowYamlInfo & fi) { fi.m_rtt_sec = t/1000000.0; node["w"] >> fi.m_w; - try { + if ( node.FindValue("cap_ipg") ){ node["cap_ipg"] >> fi.m_cap_mode; fi.m_cap_mode_was_set =true; - } catch ( const std::exception& e ) { + }else{ fi.m_cap_mode_was_set =false; } - try { + if ( node.FindValue("wlength") ){ node["wlength"] >> fi.m_wlength; fi.m_wlength_set=true; - } catch ( const std::exception& e ) { + }else{ fi.m_wlength_set=false; fi.m_wlength =500; } - try { + if ( node.FindValue("limit") ){ node["limit"] >> fi.m_limit; fi.m_limit_was_set = true; - } catch ( const std::exception& e ) { + }else{ fi.m_limit_was_set = false; fi.m_limit = 0; } - try { + if ( node.FindValue("plugin_id") ){ uint32_t plugin_val; node["plugin_id"] >> plugin_val; fi.m_plugin_id=plugin_val; - } catch ( const std::exception& e ) { + }else{ fi.m_plugin_id=0; } + fi.m_one_app_server_was_set = false; fi.m_one_app_server = false; if ( utl_yaml_read_ip_addr(node, @@ -2446,30 +2439,26 @@ void operator >> (const YAML::Node& node, CFlowYamlInfo & fi) { } - try { - int i; - const YAML::Node& dyn_pyload = node["dyn_pyload"]; - for(unsigned i=0;i<dyn_pyload.size();i++) { - CFlowYamlDpPkt fd; - dyn_pyload[i] >> fd; - if ( fi.m_dpPkt == 0 ){ - fi.m_dpPkt = new CFlowYamlDynamicPyloadPlugin(); - if (fi.m_plugin_id == 0) { - fi.m_plugin_id = mpDYN_PYLOAD; - }else{ - fprintf(stderr," plugin should be zero with dynamic pyload program"); - exit(-1); - } - } - - fd.Dump(stdout); - - fi.m_dpPkt->Add(fd); - printf(" here "); - } - } catch ( const std::exception& e ) { - fi.m_dpPkt=0; - } + if ( node.FindValue("dyn_pyload") ){ + int i; + const YAML::Node& dyn_pyload = node["dyn_pyload"]; + for(unsigned i=0;i<dyn_pyload.size();i++) { + CFlowYamlDpPkt fd; + dyn_pyload[i] >> fd; + if ( fi.m_dpPkt == 0 ){ + fi.m_dpPkt = new CFlowYamlDynamicPyloadPlugin(); + if (fi.m_plugin_id == 0) { + fi.m_plugin_id = mpDYN_PYLOAD; + }else{ + fprintf(stderr," plugin should be zero with dynamic pyload program"); + exit(-1); + } + } + fi.m_dpPkt->Add(fd); + } + }else{ + fi.m_dpPkt=0; + } } @@ -2478,13 +2467,12 @@ void operator >> (const YAML::Node& node, CFlowsYamlInfo & flows_info) { node["duration"] >> flows_info.m_duration_sec; - try { - node["generator"] >> flows_info.m_tuple_gen; - flows_info.m_tuple_gen_was_set =true; - } catch ( const std::exception& e ) { - flows_info.m_tuple_gen_was_set =false; + if ( node.FindValue("generator") ) { + node["generator"] >> flows_info.m_tuple_gen; + flows_info.m_tuple_gen_was_set =true; + }else{ + flows_info.m_tuple_gen_was_set =false; } - // m_ipv6_set will be true if and only if both src_ipv6 // and dst_ipv6 are provided. These are used to set @@ -2500,7 +2488,8 @@ void operator >> (const YAML::Node& node, CFlowsYamlInfo & flows_info) { // formed by providing src_ipv6,dst_ipv6 and specifying // {0,0,0,0,0,0xffff} flows_info.m_ipv6_set=true; - try { + + if ( node.FindValue("src_ipv6") ) { const YAML::Node& src_ipv6_info = node["src_ipv6"]; if (src_ipv6_info.size() == 6 ){ for(unsigned i=0;i<src_ipv6_info.size();i++) { @@ -2509,14 +2498,13 @@ void operator >> (const YAML::Node& node, CFlowsYamlInfo & flows_info) { node[i] >> fi; flows_info.m_src_ipv6.push_back(fi); } - }else{ - flows_info.m_ipv6_set=false; } - } catch ( const std::exception& e ) { + }else{ flows_info.m_ipv6_set=false; } - try { + + if ( node.FindValue("dst_ipv6") ) { const YAML::Node& dst_ipv6_info = node["dst_ipv6"]; if (dst_ipv6_info.size() == 6 ){ for(unsigned i=0;i<dst_ipv6_info.size();i++) { @@ -2525,67 +2513,65 @@ void operator >> (const YAML::Node& node, CFlowsYamlInfo & flows_info) { node[i] >> fi; flows_info.m_dst_ipv6.push_back(fi); } - }else{ - flows_info.m_ipv6_set=false; } - } catch ( const std::exception& e ) { + }else{ flows_info.m_ipv6_set=false; } - try { + if ( node.FindValue("cap_ipg") ) { node["cap_ipg"] >> flows_info.m_cap_mode; flows_info.m_cap_mode_set=true; - } catch ( const std::exception& e ) { + }else{ flows_info.m_cap_mode=false; flows_info.m_cap_mode_set=false; } - double t; - try { + double t=0.0; + + if ( node.FindValue("cap_ipg_min") ) { node["cap_ipg_min"] >> t ; flows_info.m_cap_ipg_min = t/1000000.0; flows_info.m_cap_ipg_min_set=true; - } catch ( const std::exception& e ) { + }else{ flows_info.m_cap_ipg_min_set=false; flows_info.m_cap_ipg_min = 20; } - try { + if ( node.FindValue("cap_override_ipg") ) { node["cap_override_ipg"] >> t; flows_info.m_cap_overide_ipg = t/1000000.0; flows_info.m_cap_overide_ipg_set = true; - } catch ( const std::exception& e ) { + }else{ flows_info.m_cap_overide_ipg_set = false; flows_info.m_cap_overide_ipg = 0; } - try { + if (node.FindValue("wlength")) { node["wlength"] >> flows_info.m_wlength; flows_info.m_wlength_set=true; - } catch ( const std::exception& e ) { + }else{ flows_info.m_wlength_set=false; flows_info.m_wlength =100; } - try { + if (node.FindValue("one_app_server")) { node["one_app_server"] >> flows_info.m_one_app_server; flows_info.m_one_app_server_was_set=true; - } catch ( const std::exception& e ) { + }else{ flows_info.m_one_app_server =false; flows_info.m_one_app_server_was_set=false; } - try { - node["vlan"] >> flows_info.m_vlan_info; - } catch ( const std::exception& e ) { - } - try { - node["mac_override_by_ip"] >> flows_info.m_mac_replace_by_ip; - } catch ( const std::exception& e ) { - flows_info.m_mac_replace_by_ip =false; + if (node.FindValue("vlan")) { + node["vlan"] >> flows_info.m_vlan_info; } + if (node.FindValue("mac_override_by_ip")) { + node["mac_override_by_ip"] >> flows_info.m_mac_replace_by_ip; + }else{ + flows_info.m_mac_replace_by_ip =false; + } const YAML::Node& mac_info = node["mac"]; for(unsigned i=0;i<mac_info.size();i++) { @@ -2593,7 +2579,7 @@ void operator >> (const YAML::Node& node, CFlowsYamlInfo & flows_info) { const YAML::Node & node =mac_info; node[i] >> fi; flows_info.m_mac_base.push_back(fi); - } + } const YAML::Node& cap_info = node["cap_info"]; for(unsigned i=0;i<cap_info.size();i++) { @@ -3177,7 +3163,7 @@ bool CFlowGenListPerThread::Create(uint32_t thread_id, /* split the clients to threads */ CTupleGenYamlInfo * tuple_gen = &m_flow_list->m_yaml_info.m_tuple_gen; - m_smart_gen.Create(0,m_thread_id,m_flow_list->is_mac_info_configured); + m_smart_gen.Create(0,m_thread_id,m_flow_list->get_is_mac_conf()); /* split the clients to threads using the mask */ CIpPortion portion; @@ -3191,7 +3177,7 @@ bool CFlowGenListPerThread::Create(uint32_t thread_id, portion.m_ip_end, get_longest_flow(i,true), get_total_kcps(i,true)*1000, - m_flow_list, + &m_flow_list->m_mac_info, tuple_gen->m_client_pool[i].m_tcp_aging_sec, tuple_gen->m_client_pool[i].m_udp_aging_sec ); @@ -3962,7 +3948,7 @@ int CFlowGenList::load_from_mac_file(std::string file_name) { printf(" ERROR no mac_file is set, file %s does not exist \n",file_name.c_str()); exit(-1); } - is_mac_info_configured = true; + m_mac_info.set_configured(true); try { std::ifstream fin((char *)file_name.c_str()); @@ -3970,7 +3956,7 @@ int CFlowGenList::load_from_mac_file(std::string file_name) { YAML::Node doc; parser.GetNextDocument(doc); - doc[0] >> m_mac_info; + doc[0] >> m_mac_info.get_mac_info(); } catch ( const std::exception& e ) { std::cout << e.what() << "\n"; m_mac_info.clear(); @@ -3982,7 +3968,6 @@ int CFlowGenList::load_from_mac_file(std::string file_name) { int CFlowGenList::load_from_yaml(std::string file_name, uint32_t num_threads){ - is_mac_info_configured = false; uint8_t idx; m_yaml_info.load_from_yaml_file(file_name); if (m_yaml_info.verify_correctness(num_threads) ==false){ @@ -6643,25 +6628,6 @@ void CFlowYamlDynamicPyloadPlugin::Dump(FILE *fd){ } } -bool is_mac_info_conf(CFlowGenList *fl_list) { - if (fl_list) { - return fl_list->is_mac_info_configured; - } - return false; -} - -mac_addr_align_t * get_mac_addr_by_ip(CFlowGenList *fl_list, - uint32_t ip) { - if (fl_list && - fl_list->is_mac_info_configured && - fl_list->m_mac_info.count(ip)>0) { - return &fl_list->m_mac_info[ip]; - } - return NULL; -} - - - uint16_t CSimplePacketParser::getPktSize(){ uint16_t ip_len=0; if (m_ipv4) { diff --git a/src/bp_sim.h b/src/bp_sim.h index 29b9a724..b7cfb20b 100755 --- a/src/bp_sim.h +++ b/src/bp_sim.h @@ -221,7 +221,9 @@ private: memset(m_pyload_mbuf_ptr+len+m_new_pkt_size,0xa,(-m_new_pkt_size)); } + return (0); } + public: int16_t m_new_pkt_size; /* New packet size after transform by plugin */ CFlowPktInfo * m_pkt_info; @@ -302,7 +304,7 @@ public: void CVirtualIFPerSideStats::Dump(FILE *fd){ - #define DP_B(f) if (f) printf(" %-40s : %llu \n",#f,f) + #define DP_B(f) if (f) printf(" %-40s : %lu \n",#f,f) DP_B(m_tx_pkt); DP_B(m_tx_rx_check_pkt); DP_B(m_tx_bytes); @@ -688,6 +690,15 @@ public: RUN_FLAGS_RXCHECK_CONST_TS =1, }; + /** + * different running modes for Trex + */ + enum trex_run_mode_e { + RUN_MODE_INVALID, + RUN_MODE_BATCH, + RUN_MODE_INTERACTIVE + }; + public: CParserOption(){ m_factor=1.0; @@ -707,6 +718,7 @@ public: m_run_flags=0; prefix=""; m_mac_splitter=0; + m_run_mode = RUN_MODE_INVALID; } CPreviewMode preview; @@ -730,13 +742,14 @@ public: uint8_t m_mac_splitter; uint8_t m_pad; + trex_run_mode_e m_run_mode; - std::string cfg_file; - std::string mac_file; - std::string platform_cfg_file; + std::string cfg_file; + std::string mac_file; + std::string platform_cfg_file; - std::string out_file; - std::string prefix; + std::string out_file; + std::string prefix; CMacAddrCfg m_mac_addr[MAX_LATENCY_PORTS]; @@ -1429,7 +1442,7 @@ public: inline bool is_eligible_from_server_side(){ - return ( (m_src_ip&1==1)?true:false); + return ( ( (m_src_ip&1) == 1)?true:false); } @@ -1636,7 +1649,7 @@ public: */ inline int check_objects_sizes(void){ if ( sizeof(CGenNodeDeferPort) != sizeof(CGenNode) ) { - printf("ERROR sizeof(CGenNodeDeferPort) %d != sizeof(CGenNode) %d must be the same size \n",sizeof(CGenNodeDeferPort),sizeof(CGenNode)); + printf("ERROR sizeof(CGenNodeDeferPort) %lu != sizeof(CGenNode) %lu must be the same size \n",sizeof(CGenNodeDeferPort),sizeof(CGenNode)); assert(0); } if ( (int)offsetof(struct CGenNodeDeferPort,m_type)!=offsetof(struct CGenNode,m_type) ){ @@ -2576,6 +2589,8 @@ inline void CFlowPktInfo::update_pkt_info2(char *p, EthernetHeader * et = (EthernetHeader * )(p + m_pkt_indication.getFastEtherOffset()); + (void)et; + if ( unlikely (m_pkt_indication.is_ipv6())) { IPv6Header *ipv6= (IPv6Header *)ipv4; @@ -2658,6 +2673,8 @@ inline void CFlowPktInfo::update_pkt_info(char *p, EthernetHeader * et = (EthernetHeader * )(p + m_pkt_indication.getFastEtherOffset()); + (void)et; + uint16_t src_port = node->m_src_port; pkt_dir_t ip_dir = node->cur_pkt_ip_addr_dir(); @@ -2858,6 +2875,7 @@ inline rte_mbuf_t * CFlowPktInfo::do_generate_new_mbuf_ex_vm(CGenNode * node, /* need to update the mbuf size here .., this is not must but needed for accuracy */ uint16_t buf_adjust = len - vm.m_new_pkt_size; int rc = rte_pktmbuf_trim(m, buf_adjust); + (void)rc; /* update IP length , and TCP checksum , we can accelerate this using hardware ! */ uint16_t pkt_adjust = vm.m_new_pkt_size - m_packet->pkt_len; @@ -3430,6 +3448,7 @@ inline void CFlowGenListPerThread::free_last_flow_node(CGenNode *p){ free_node( p); } + class CFlowGenList { public: @@ -3457,12 +3476,12 @@ public: double get_total_tx_bps(); uint32_t get_total_repeat_flows(); double get_delta_flow_is_sec(); + bool get_is_mac_conf() { return m_mac_info.is_configured();} public: std::vector<CFlowGeneratorRec *> m_cap_gen; /* global info */ CFlowsYamlInfo m_yaml_info; /* global yaml*/ std::vector<CFlowGenListPerThread *> m_threads_info; - bool is_mac_info_configured; - std::map<uint32_t, mac_addr_align_t> m_mac_info; /* global mac info loaded form mac_file*/ + CFlowGenListMac m_mac_info; }; diff --git a/src/gtest/rpc_test.cpp b/src/gtest/rpc_test.cpp index 38d34320..250d5342 100644 --- a/src/gtest/rpc_test.cpp +++ b/src/gtest/rpc_test.cpp @@ -42,11 +42,6 @@ protected: m_verbose = false; - TrexRpcServerConfig cfg = TrexRpcServerConfig(TrexRpcServerConfig::RPC_PROT_TCP, 5050); - - m_rpc = new TrexRpcServer(cfg); - m_rpc->start(); - m_context = zmq_ctx_new (); m_socket = zmq_socket (m_context, ZMQ_REQ); zmq_connect (m_socket, "tcp://localhost:5050"); @@ -54,9 +49,6 @@ protected: } virtual void TearDown() { - m_rpc->stop(); - - delete m_rpc; zmq_close(m_socket); zmq_term(m_context); } @@ -657,3 +649,4 @@ TEST_F(RpcTestOwned, start_stop_traffic) { send_request(request, response); EXPECT_EQ(response["result"], "ACK"); } + diff --git a/src/gtest/tuple_gen_test.cpp b/src/gtest/tuple_gen_test.cpp index 8791b67d..8a774e38 100755 --- a/src/gtest/tuple_gen_test.cpp +++ b/src/gtest/tuple_gen_test.cpp @@ -334,7 +334,8 @@ TEST(tuple_gen,GenerateTupleMac) { CClientPool gen; gen.Create(cdSEQ_DIST, - 0x10000001, 0x1000000f, 64000,2, &fl,true,0,0); + 0x10000001, 0x1000000f, 64000,2, &fl.m_mac_info,true,0,0); + CTupleBase result; uint32_t result_src; uint16_t result_port; @@ -348,6 +349,7 @@ TEST(tuple_gen,GenerateTupleMac) { result_mac = result.getClientMac(); EXPECT_EQ(result_src, (uint32_t)(0x10000001+i%2)); EXPECT_EQ(result_port, 1024+i/2); + printf("i:%d,mac:%d\n",i,result_mac->mac[3]); if (i%2==0) EXPECT_EQ(result_mac->mac[3], 5); else diff --git a/src/mac_mapping.h b/src/mac_mapping.h new file mode 100644 index 00000000..84151e8c --- /dev/null +++ b/src/mac_mapping.h @@ -0,0 +1,60 @@ +#ifndef MAC_MAPPING_H_ +#define MAC_MAPPING_H_ + +#define INUSED 0 +#define UNUSED 1 +typedef struct mac_addr_align_ { +public: + uint8_t mac[6]; + uint8_t inused; + uint8_t pad; +} mac_addr_align_t; + +typedef struct mac_mapping_ { + mac_addr_align_t mac; + uint32_t ip; +} mac_mapping_t; + +class CFlowGenListMac { +public: + CFlowGenListMac() { + set_configured(false); + } + + std::map<uint32_t, mac_addr_align_t> & + get_mac_info () { + return m_mac_info; + } + + bool is_configured() { + return is_mac_info_configured; + } + + void set_configured(bool is_conf) { + is_mac_info_configured = is_conf; + } + + void clear() { + set_configured(false); + m_mac_info.clear(); + } + + uint32_t is_mac_exist(uint32_t ip) { + if (is_configured()) { + return m_mac_info.count(ip); + } else { + return 0; + } + } + mac_addr_align_t* get_mac_addr_by_ip(uint32_t ip) { + if (is_mac_exist(ip)!=0) { + return &(m_mac_info[ip]); + } + return NULL; + } +private: + bool is_mac_info_configured; + std::map<uint32_t, mac_addr_align_t> m_mac_info; /* global mac info loaded form mac_file*/ +}; + +#endif //MAC_MAPPING_H_ diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 820fb3fa..a0af9fdf 100755 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -55,6 +55,7 @@ limitations under the License. #include <common/arg/SimpleGlob.h> #include <common/arg/SimpleOpt.h> #include <common/basic_utils.h> +#include <stateless/cp/trex_stateless.h> #include <../linux_dpdk/version.h> extern "C" { @@ -426,7 +427,8 @@ static char global_loglevel_str[20]; // cores =0==1,1*2,2,3,4,5,6 // An enum for all the option types enum { OPT_HELP, - OPT_CFG, + OPT_MODE_BATCH, + OPT_MODE_INTERACTIVE, OPT_NODE_DUMP, OPT_UT, OPT_FILE_OUT, @@ -478,15 +480,16 @@ enum { OPT_HELP, */ static CSimpleOpt::SOption parser_options[] = { - { OPT_HELP, "-?", SO_NONE }, - { OPT_HELP, "-h", SO_NONE }, - { OPT_HELP, "--help", SO_NONE }, - { OPT_UT, "--ut", SO_NONE }, - { OPT_CFG, "-f", SO_REQ_SEP}, - { OPT_PLAT_CFG_FILE,"--cfg", SO_REQ_SEP}, - { OPT_REAL_TIME , "-r", SO_NONE }, - { OPT_SINGLE_CORE , "-s", SO_NONE }, - { OPT_FILE_OUT , "-o" , SO_REQ_SEP}, + { OPT_HELP, "-?", SO_NONE }, + { OPT_HELP, "-h", SO_NONE }, + { OPT_HELP, "--help", SO_NONE }, + { OPT_UT, "--ut", SO_NONE }, + { OPT_MODE_BATCH, "-f", SO_REQ_SEP}, + { OPT_MODE_INTERACTIVE, "-i", SO_NONE }, + { OPT_PLAT_CFG_FILE, "--cfg", SO_REQ_SEP}, + { OPT_REAL_TIME , "-r", SO_NONE }, + { OPT_SINGLE_CORE, "-s", SO_NONE }, + { OPT_FILE_OUT, "-o" , SO_REQ_SEP}, { OPT_FLIP_CLIENT_SERVER,"--flip",SO_NONE }, { OPT_FLOW_FLIP_CLIENT_SERVER,"-p",SO_NONE }, { OPT_FLOW_FLIP_CLIENT_SERVER_SIDE,"-e",SO_NONE }, @@ -533,13 +536,18 @@ static CSimpleOpt::SOption parser_options[] = static int usage(){ - printf(" Usage: t-rex-64 [OPTION] -f cfg.yaml -c cores \n"); + printf(" Usage: t-rex-64 [MODE] [OPTION] -f cfg.yaml -c cores \n"); printf(" \n"); printf(" \n"); - printf(" options \n"); + + printf(" mode \n\n"); printf(" -f [file] : YAML file with template configuration \n"); + printf(" -i : launch TRex in interactive mode (RPC server)\n"); printf(" \n\n"); - printf(" --mac [file] : YAML file with <client ip, mac addr> configuration \n"); + + printf(" options \n\n"); + + printf(" --mac [file] : YAML file with <client ip, mac addr> configuration \n"); printf(" \n\n"); printf(" -r : realtime enable \n"); printf(" \n\n"); @@ -612,7 +620,8 @@ static int usage(){ printf(" --mac-spread : Spread the destination mac-order by this factor. e.g 2 will generate the traffic to 2 devices DEST-MAC ,DEST-MAC+1 \n"); printf(" maximum is up to 128 devices \n"); - printf(" simulation mode : \n"); + + printf("\n simulation mode : \n"); printf(" Using this mode you can generate the traffic into a pcap file and learn how trex works \n"); printf(" With this version you must be SUDO to use this mode ( I know this is not normal ) \n"); printf(" you can use the Linux CEL version of t-rex to do it without super user \n"); @@ -653,6 +662,7 @@ static int usage(){ printf(" Open Source Components / Libraries \n"); printf(" DPDK (BSD) \n"); printf(" YAML-CPP (BSD) \n"); + printf(" JSONCPP (MIT) \n"); printf(" \n"); printf(" Open Source Binaries \n"); printf(" ZMQ (LGPL v3plus) \n"); @@ -667,6 +677,11 @@ static int usage(){ int gtest_main(int argc, char **argv) ; +static void parse_err(const std::string &msg) { + std::cout << "\nArgument Parsing Error: \n\n" << "*** "<< msg << "\n\n"; + exit(-1); +} + static int parse_options(int argc, char *argv[], CParserOption* po, bool first_time ) { CSimpleOpt args(argc, argv, parser_options); @@ -679,36 +694,55 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t int res1; uint32_t tmp_data; + po->m_run_mode = CParserOption::RUN_MODE_INVALID; while ( args.Next() ){ if (args.LastError() == SO_SUCCESS) { switch (args.OptionId()) { + case OPT_UT : - printf(" Supported only in simulation \n"); - res1=0; - exit(res1); + parse_err("Supported only in simulation"); break; + case OPT_HELP: usage(); return -1; - case OPT_CFG: + + case OPT_MODE_BATCH: + if (po->m_run_mode != CParserOption::RUN_MODE_INVALID) { + parse_err("Please specify single run mode"); + } + po->m_run_mode = CParserOption::RUN_MODE_BATCH; po->cfg_file = args.OptionArg(); break; + + case OPT_MODE_INTERACTIVE: + if (po->m_run_mode != CParserOption::RUN_MODE_INVALID) { + parse_err("Please specify single run mode"); + } + po->m_run_mode = CParserOption::RUN_MODE_INTERACTIVE; + break; + case OPT_NO_KEYBOARD_INPUT : po->preview.set_no_keyboard(true); break; + case OPT_MAC_FILE : po->mac_file = args.OptionArg(); break; + case OPT_PLAT_CFG_FILE : po->platform_cfg_file = args.OptionArg(); break; + case OPT_SINGLE_CORE : po->preview.setSingleCore(true); break; + case OPT_IPV6: po->preview.set_ipv6_mode_enable(true); break; + case OPT_VLAN: po->preview.set_vlan_mode_enable(true); break; @@ -726,6 +760,7 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t printf(" warning -r is deprecated, real time is not needed any more , it is the default \n"); po->preview.setRealTime(true); break; + case OPT_NO_FLOW_CONTROL: po->preview.set_disable_flow_control_setting(true); break; @@ -832,21 +867,20 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t } // End of while - if ((po->cfg_file =="") ) { - printf("Invalid combination of parameters you must add -f with configuration file \n"); - return -1; + if ((po->m_run_mode == CParserOption::RUN_MODE_INVALID) ) { + parse_err("Please provide single run mode (e.g. batch or interactive)"); } if ( po->m_mac_splitter > 128 ){ - printf("maximum mac spreading is 128 you set it to %d \n",po->m_mac_splitter); - return -1; + std::stringstream ss; + ss << "maximum mac spreading is 128 you set it to: " << po->m_mac_splitter; + parse_err(ss.str()); } if ( po->preview.get_learn_mode_enable() ){ if ( po->preview.get_ipv6_mode_enable() ){ - printf("--learn mode is not supported with --ipv6, beacuse there is not such thing NAT66 ( ipv6-ipv6) \n"); - printf("if you think it is important,open a defect \n"); - return -1; + parse_err("--learn mode is not supported with --ipv6, beacuse there is not such thing NAT66 ( ipv6-ipv6) \n" \ + "if you think it is important,open a defect \n"); } if ( po->is_latency_disabled() ){ /* set latency thread */ @@ -2701,7 +2735,7 @@ public: } public: - bool Create(); + bool Create(bool is_stateless); void Delete(); int ixgbe_prob_init(); @@ -3375,18 +3409,22 @@ int CGlobalPortCfg::ixgbe_start(void){ } -bool CGlobalPortCfg::Create(){ - - if ( !m_zmq_publisher.Create( CGlobalInfo::m_options.m_zmq_port, - !CGlobalInfo::m_options.preview.get_zmq_publish_enable() ) ){ - return (false); - } +bool CGlobalPortCfg::Create(bool is_stateless){ + /* hack - need to refactor */ + if (!is_stateless) { + if ( !m_zmq_publisher.Create( CGlobalInfo::m_options.m_zmq_port, + !CGlobalInfo::m_options.preview.get_zmq_publish_enable() ) ){ + return (false); + } + } /* We load the YAML twice, this is the first time. to update global flags */ CFlowsYamlInfo pre_yaml_info; - pre_yaml_info.load_from_yaml_file(CGlobalInfo::m_options.cfg_file); + if (!is_stateless) { + pre_yaml_info.load_from_yaml_file(CGlobalInfo::m_options.cfg_file); + } if ( pre_yaml_info.m_vlan_info.m_enable ){ CGlobalInfo::m_options.preview.set_vlan_mode_enable(true); @@ -3863,6 +3901,7 @@ int CGlobalPortCfg::run_in_master(){ std::string json; bool was_stopped=false; + while ( true ) { if ( CGlobalInfo::m_options.preview.get_no_keyboard() ==false ){ @@ -3976,6 +4015,7 @@ int CGlobalPortCfg::run_in_master(){ break; } } + m_mg.stop(); delay(1000); if ( was_stopped ){ @@ -4100,9 +4140,9 @@ int CGlobalPortCfg::start_send_master(){ if (CGlobalInfo::m_options.mac_file != "") { CGlobalInfo::m_options.preview.set_mac_ip_mapping_enable(true); m_fl.load_from_mac_file(CGlobalInfo::m_options.mac_file); - m_fl.is_mac_info_configured = true; + m_fl.m_mac_info.set_configured(true); } else { - m_fl.is_mac_info_configured = false; + m_fl.m_mac_info.set_configured(false); } m_expected_pps = m_fl.get_total_pps(); @@ -4172,7 +4212,18 @@ static int latency_one_lcore(__attribute__((unused)) void *dummy) } +static int stateless_entry(__attribute__((unused)) void *dummy) { + CPlatformSocketInfo * lpsock=&CGlobalInfo::m_socket; + physical_thread_id_t phy_id = rte_lcore_id(); + + if (lpsock->thread_phy_is_master( phy_id )) { + TrexStateless::get_instance().launch_control_plane(); + } else { + TrexStateless::get_instance().launch_on_dp_core(phy_id); + } + return (0); +} static int slave_one_lcore(__attribute__((unused)) void *dummy) { @@ -4381,7 +4432,37 @@ int sim_load_list_of_cap_files(CParserOption * op){ +static int +launch_stateless_trex() { + CPlatformSocketInfo *lpsock=&CGlobalInfo::m_socket; + CParserOption *lpop= &CGlobalInfo::m_options; + CPlatformYamlInfo *cg=&global_platform_cfg_info; + + TrexStatelessCfg cfg; + + TrexRpcServerConfig rpc_req_resp_cfg(TrexRpcServerConfig::RPC_PROT_TCP, 5050); + TrexRpcServerConfig rpc_async_cfg(TrexRpcServerConfig::RPC_PROT_TCP, 5051); + cfg.m_dp_core_count = lpop->preview.getCores(); + cfg.m_port_count = lpop->m_expected_portd; + cfg.m_rpc_req_resp_cfg = &rpc_req_resp_cfg; + cfg.m_rpc_async_cfg = &rpc_async_cfg; + cfg.m_rpc_server_verbose = true; + + TrexStateless::configure(cfg); + + printf("\nStarting T-Rex Stateless\n"); + printf("Starting RPC Server...\n\n"); + + rte_eal_mp_remote_launch(stateless_entry, NULL, CALL_MASTER); + + unsigned lcore_id; + RTE_LCORE_FOREACH_SLAVE(lcore_id) { + if (rte_eal_wait_lcore(lcore_id) < 0) + return -1; + } + return (0); +} @@ -4436,7 +4517,6 @@ int main_test(int argc , char * argv[]){ rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n"); } - time_init(); /* check if we are in simulation mode */ @@ -4445,11 +4525,18 @@ int main_test(int argc , char * argv[]){ return ( sim_load_list_of_cap_files(&CGlobalInfo::m_options) ); } + bool is_stateless = (CGlobalInfo::m_options.m_run_mode == CParserOption::RUN_MODE_INTERACTIVE); - if ( !ports_cfg.Create() ){ + if ( !ports_cfg.Create(is_stateless) ){ exit(1); } + /* patch here */ + if (is_stateless) { + return launch_stateless_trex(); + } + + if (po->preview.get_is_rx_check_enable() && (po->m_rx_check_sampe< get_min_sample_rate()) ) { po->m_rx_check_sampe = get_min_sample_rate(); printf("Warning rx check sample rate should be lower than %d setting it to %d\n",get_min_sample_rate(),get_min_sample_rate()); @@ -4518,6 +4605,7 @@ int main_test(int argc , char * argv[]){ if (rte_eal_wait_lcore(lcore_id) < 0) return -1; } + ports_cfg.stop_master(); ports_cfg.Delete(); utl_termio_reset(); diff --git a/src/mock/rte_ethdev.h b/src/mock/rte_ethdev.h new file mode 100644 index 00000000..046d8366 --- /dev/null +++ b/src/mock/rte_ethdev.h @@ -0,0 +1,44 @@ +/* + 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 __MOCK_FILE_RTE_ETHDEV_H__ +#define __MOCK_FILE_RTE_ETHDEV_H__ + +#include <string.h> + +struct rte_eth_stats { + uint64_t obytes; + uint64_t ibytes; + uint64_t opackets; + uint64_t ipackets; +}; + +static inline void +rte_eth_stats_get(uint8_t port_id, struct rte_eth_stats *stats) { + memset(stats, 0, sizeof(rte_eth_stats)); +} + +static inline uint16_t +rte_eth_tx_burst(uint8_t port_id, uint16_t queue_id, + struct rte_mbuf **tx_pkts, uint16_t nb_pkts) { + return (0); +} + +#endif /* __MOCK_FILE_RTE_ETHDEV_H__ */ diff --git a/src/rpc-server/trex_rpc_server_mock.cpp b/src/mock/trex_rpc_server_mock.cpp index 835e28b8..de43f92f 100644 --- a/src/rpc-server/trex_rpc_server_mock.cpp +++ b/src/mock/trex_rpc_server_mock.cpp @@ -20,7 +20,7 @@ limitations under the License. */ #include <trex_rpc_server_api.h> -#include <trex_stateless_api.h> +#include <trex_stateless.h> #include <iostream> #include <unistd.h> @@ -44,29 +44,42 @@ int gtest_main(int argc, char **argv); int main(int argc, char *argv[]) { - /* configure the stateless object with 4 ports */ - TrexStateless::configure(4); + bool is_gtest = false; - // gtest ? + // gtest ? if (argc > 1) { if (string(argv[1]) != "--ut") { cout << "\n[Usage] " << argv[0] << ": " << " [--ut]\n\n"; exit(-1); } - return gtest_main(argc, argv); + is_gtest = true; } - cout << "\n-= Starting RPC Server Mock =-\n\n"; - cout << "Listening on tcp://localhost:5050 [ZMQ]\n\n"; + /* configure the stateless object with 4 ports */ + TrexStatelessCfg cfg; - TrexRpcServerConfig rpc_cfg(TrexRpcServerConfig::RPC_PROT_TCP, 5050); - TrexRpcServer rpc(rpc_cfg); + TrexRpcServerConfig rpc_req_resp_cfg(TrexRpcServerConfig::RPC_PROT_TCP, 5050); + TrexRpcServerConfig rpc_async_cfg(TrexRpcServerConfig::RPC_PROT_TCP, 5051); - /* init the RPC server */ - rpc.start(); + cfg.m_port_count = 4; + cfg.m_dp_core_count = 2; + cfg.m_rpc_req_resp_cfg = &rpc_req_resp_cfg; + cfg.m_rpc_async_cfg = &rpc_async_cfg; + cfg.m_rpc_server_verbose = (is_gtest ? false : true); - cout << "Setting Server To Full Verbose\n\n"; - rpc.set_verbose(true); + TrexStateless::configure(cfg); + + TrexStateless::get_instance().launch_control_plane(); + + /* gtest handling */ + if (is_gtest) { + int rc = gtest_main(argc, argv); + TrexStateless::destroy(); + return rc; + } + + cout << "\n-= Starting RPC Server Mock =-\n\n"; + cout << "Listening on tcp://localhost:5050 [ZMQ]\n\n"; cout << "Server Started\n\n"; @@ -74,7 +87,6 @@ int main(int argc, char *argv[]) { sleep(1); } - rpc.stop(); - - + TrexStateless::destroy(); } + diff --git a/src/platform_cfg.cpp b/src/platform_cfg.cpp index a226a9ac..f0911611 100755 --- a/src/platform_cfg.cpp +++ b/src/platform_cfg.cpp @@ -189,107 +189,70 @@ void operator >> (const YAML::Node& node, CMacYamlInfo & mac_info) { } void operator >> (const YAML::Node& node, CPlatformMemoryYamlInfo & plat_info) { - try { - node["mbuf_64"] >> plat_info.m_mbuf[MBUF_64]; - } catch ( const std::exception& e ) { - } - try { - node["mbuf_128"] >> plat_info.m_mbuf[MBUF_128]; - } catch ( const std::exception& e ) { + if ( node.FindValue("mbuf_64") ){ + node["mbuf_64"] >> plat_info.m_mbuf[MBUF_64]; } - try { - node["mbuf_256"] >> plat_info.m_mbuf[MBUF_256]; - } catch ( const std::exception& e ) { + if ( node.FindValue("mbuf_128") ){ + node["mbuf_128"] >> plat_info.m_mbuf[MBUF_128]; } - try { - node["mbuf_512"] >> plat_info.m_mbuf[MBUF_512]; - } catch ( const std::exception& e ) { + if ( node.FindValue("mbuf_256") ){ + node["mbuf_256"] >> plat_info.m_mbuf[MBUF_256]; } - try { - node["mbuf_1024"] >> plat_info.m_mbuf[MBUF_1024]; - } catch ( const std::exception& e ) { + if ( node.FindValue("mbuf_512") ){ + node["mbuf_512"] >> plat_info.m_mbuf[MBUF_512]; } - try { - node["mbuf_2048"] >> plat_info.m_mbuf[MBUF_2048]; - } catch ( const std::exception& e ) { + if ( node.FindValue("mbuf_1024") ){ + node["mbuf_1024"] >> plat_info.m_mbuf[MBUF_1024]; } - try { - node["traffic_mbuf_64"] >> plat_info.m_mbuf[TRAFFIC_MBUF_64]; - } catch ( const std::exception& e ) { + if ( node.FindValue("mbuf_2048") ){ + node["mbuf_2048"] >> plat_info.m_mbuf[MBUF_2048]; } - try { - node["traffic_mbuf_128"] >> plat_info.m_mbuf[TRAFFIC_MBUF_128]; - } catch ( const std::exception& e ) { + if ( node.FindValue("traffic_mbuf_64") ){ + node["traffic_mbuf_64"] >> plat_info.m_mbuf[TRAFFIC_MBUF_64]; } - try { - node["traffic_mbuf_256"] >> plat_info.m_mbuf[TRAFFIC_MBUF_256]; - } catch ( const std::exception& e ) { + if ( node.FindValue("traffic_mbuf_128") ){ + node["traffic_mbuf_128"] >> plat_info.m_mbuf[TRAFFIC_MBUF_128]; } - try { - node["traffic_mbuf_512"] >> plat_info.m_mbuf[TRAFFIC_MBUF_512]; - } catch ( const std::exception& e ) { + if ( node.FindValue("traffic_mbuf_256") ){ + node["traffic_mbuf_256"] >> plat_info.m_mbuf[TRAFFIC_MBUF_256]; } - try { - node["traffic_mbuf_1024"] >> plat_info.m_mbuf[TRAFFIC_MBUF_1024]; - } catch ( const std::exception& e ) { + if ( node.FindValue("traffic_mbuf_512") ){ + node["traffic_mbuf_512"] >> plat_info.m_mbuf[TRAFFIC_MBUF_512]; } - try { - node["traffic_mbuf_2048"] >> plat_info.m_mbuf[TRAFFIC_MBUF_2048]; - } catch ( const std::exception& e ) { + if ( node.FindValue("traffic_mbuf_1024") ){ + node["traffic_mbuf_1024"] >> plat_info.m_mbuf[TRAFFIC_MBUF_1024]; } - try { - node["dp_flows"] >> plat_info.m_mbuf[MBUF_DP_FLOWS]; - } catch ( const std::exception& e ) { + if ( node.FindValue("traffic_mbuf_2048") ){ + node["traffic_mbuf_2048"] >> plat_info.m_mbuf[TRAFFIC_MBUF_2048]; } - try { - node["global_flows"] >> plat_info.m_mbuf[MBUF_GLOBAL_FLOWS]; - } catch ( const std::exception& e ) { + if ( node.FindValue("dp_flows") ){ + node["dp_flows"] >> plat_info.m_mbuf[MBUF_DP_FLOWS]; } -} - -void operator >> (const YAML::Node& node, CPlatformYamlInfo & plat_info) { - try { - node["port_limit"] >> plat_info.m_port_limit; - plat_info.m_port_limit_exist=true; - } catch ( const std::exception& e ) { - plat_info.m_port_limit=0xffffffff; + if ( node.FindValue("global_flows") ){ + node["global_flows"] >> plat_info.m_mbuf[MBUF_GLOBAL_FLOWS]; } +} - try { - const YAML::Node& interface_mask = node["interface_mask"]; - for(unsigned i=0;i<interface_mask.size();i++) { - std::string fi; - const YAML::Node & node = interface_mask; - node[i] >> fi; - plat_info.m_if_mask.push_back(fi); - } - plat_info.m_if_mask_exist=true; - } catch ( const std::exception& e ) { - - } +void operator >> (const YAML::Node& node, CPlatformYamlInfo & plat_info) { - try { - node["enable_zmq_pub"] >> plat_info.m_enable_zmq_pub; - node["zmq_pub_port"] >> plat_info.m_zmq_pub_port; - plat_info.m_enable_zmq_pub_exist = true; - } catch ( const std::exception& e ) { - plat_info.m_enable_zmq_pub_exist = false; + if (node.FindValue("interface_mask")) { + printf("WARNING interface_mask in not used any more !\n"); } /* must have interfaces */ @@ -301,31 +264,44 @@ void operator >> (const YAML::Node& node, CPlatformYamlInfo & plat_info) { plat_info.m_if_list.push_back(fi); } - try { - node["prefix"] >> plat_info.m_prefix; - } catch ( const std::exception& e ) { + + if ( node.FindValue("port_limit") ){ + node["port_limit"] >> plat_info.m_port_limit; + plat_info.m_port_limit_exist=true; } - try { - node["limit_memory"] >> plat_info.m_limit_memory; - } catch ( const std::exception& e ) { + + + plat_info.m_enable_zmq_pub_exist = true; + + if ( node.FindValue("enable_zmq_pub") ){ + node["enable_zmq_pub"] >> plat_info.m_enable_zmq_pub; + plat_info.m_enable_zmq_pub_exist = true; } - try { - node["c"] >> plat_info.m_thread_per_dual_if; - } catch ( const std::exception& e ) { + + if ( node.FindValue("zmq_pub_port") ){ + node["zmq_pub_port"] >> plat_info.m_zmq_pub_port; + plat_info.m_enable_zmq_pub_exist = true; + } + + if ( node.FindValue("prefix") ){ + node["prefix"] >> plat_info.m_prefix; } + if ( node.FindValue("limit_memory") ){ + node["limit_memory"] >> plat_info.m_limit_memory; + } + if ( node.FindValue("c") ){ + node["c"] >> plat_info.m_thread_per_dual_if; + } - try { - node["telnet_port"] >> plat_info.m_telnet_port; - plat_info.m_telnet_exist=true; - } catch ( const std::exception& e ) { - plat_info.m_telnet_port=4501; + if ( node.FindValue("telnet_port") ){ + node["telnet_port"] >> plat_info.m_telnet_port; + plat_info.m_telnet_exist=true; } - try { - node["port_bandwidth_gb"] >> plat_info.m_port_bandwidth_gb; - } catch ( const std::exception& e ) { + if ( node.FindValue("port_bandwidth_gb") ){ + node["port_bandwidth_gb"] >> plat_info.m_port_bandwidth_gb; } if ( node.FindValue("memory") ){ @@ -337,7 +313,7 @@ void operator >> (const YAML::Node& node, CPlatformYamlInfo & plat_info) { plat_info.m_platform.m_is_exists=true; } - try { + if ( node.FindValue("port_info") ) { const YAML::Node& mac_info = node["port_info"]; for(unsigned i=0;i<mac_info.size();i++) { CMacYamlInfo fi; @@ -346,7 +322,6 @@ void operator >> (const YAML::Node& node, CPlatformYamlInfo & plat_info) { plat_info.m_mac_info.push_back(fi); } plat_info.m_mac_info_exist = true; - }catch ( const std::exception& e ) { } } diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp index 0c9f2c49..ae87d749 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp @@ -21,7 +21,8 @@ limitations under the License. #include "trex_rpc_cmds.h" #include <trex_rpc_server_api.h> -#include <trex_stateless_api.h> +#include <trex_stateless.h> +#include <trex_stateless_port.h> #include <trex_rpc_cmds_table.h> #include <fstream> @@ -154,7 +155,7 @@ TrexRpcCmdGetSysInfo::_run(const Json::Value ¶ms, Json::Value &result) { section["uptime"] = TrexRpcServer::get_server_uptime(); /* FIXME: core count */ - section["dp_core_count"] = 1; + section["dp_core_count"] = instance.get_dp_core_count(); section["core_type"] = get_cpu_model(); /* ports */ @@ -271,17 +272,7 @@ TrexRpcCmdGetPortStats::_run(const Json::Value ¶ms, Json::Value &result) { result["result"]["status"] = port->get_state_as_string(); - result["result"]["tx_bps"] = Json::Value::UInt64(port->get_port_stats().tx_bps); - result["result"]["tx_pps"] = Json::Value::UInt64(port->get_port_stats().tx_pps); - result["result"]["total_tx_pkts"] = Json::Value::UInt64(port->get_port_stats().total_tx_pkts); - result["result"]["total_tx_bytes"] = Json::Value::UInt64(port->get_port_stats().total_tx_bytes); - - result["result"]["rx_bps"] = Json::Value::UInt64(port->get_port_stats().rx_bps); - result["result"]["rx_pps"] = Json::Value::UInt64(port->get_port_stats().rx_pps); - result["result"]["total_rx_pkts"] = Json::Value::UInt64(port->get_port_stats().total_rx_pkts); - result["result"]["total_rx_bytes"] = Json::Value::UInt64(port->get_port_stats().total_rx_bytes); - - result["result"]["tx_rx_error"] = Json::Value::UInt64(port->get_port_stats().tx_rx_errors); + port->encode_stats(result["result"]); return (TREX_RPC_CMD_OK); } diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index 1450e1a9..20107411 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -1,5 +1,5 @@ /* - Itay Marom + Itay Marom, Dan Klein Cisco Systems, Inc. */ @@ -20,8 +20,9 @@ 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 <trex_stream.h> +#include <trex_stateless.h> +#include <trex_stateless_port.h> #include <iostream> @@ -107,7 +108,7 @@ TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { 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); + stream->m_rx_check.m_latency = parse_bool(rx, "latency_enabled", result); } /* make sure this is a valid stream to add */ @@ -232,7 +233,7 @@ TrexRpcCmdAddStream::parse_vm_instr_flow_var(const Json::Value &inst, TrexStream 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); + std::string flow_var_name = parse_string(inst, "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); diff --git a/src/rpc-server/trex_rpc_async_server.cpp b/src/rpc-server/trex_rpc_async_server.cpp new file mode 100644 index 00000000..f4d21f2f --- /dev/null +++ b/src/rpc-server/trex_rpc_async_server.cpp @@ -0,0 +1,109 @@ +/* + 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. +*/ + +/* required for sleep_for c++ 2011 + https://bugs.launchpad.net/ubuntu/+source/gcc-4.4/+bug/608145 +*/ +#define _GLIBCXX_USE_NANOSLEEP + +#include <trex_stateless.h> +#include <trex_stateless_port.h> +#include <trex_rpc_async_server.h> +#include <zmq.h> +#include <json/json.h> +#include <string> +#include <iostream> + +/** + * ZMQ based publisher server + * + */ +TrexRpcServerAsync::TrexRpcServerAsync(const TrexRpcServerConfig &cfg, std::mutex *lock) : TrexRpcServerInterface(cfg, "publisher", lock) { + /* ZMQ is not thread safe - this should be outside */ + m_context = zmq_ctx_new(); +} + +/** + * publisher thread + * + */ +void +TrexRpcServerAsync::_rpc_thread_cb() { + std::stringstream ss; + + /* create a socket based on the configuration */ + m_socket = zmq_socket (m_context, ZMQ_PUB); + + switch (m_cfg.get_protocol()) { + case TrexRpcServerConfig::RPC_PROT_TCP: + ss << "tcp://*:"; + break; + default: + throw TrexRpcException("unknown protocol for RPC"); + } + + ss << m_cfg.get_port(); + + /* bind the scoket */ + int rc = zmq_bind (m_socket, ss.str().c_str()); + if (rc != 0) { + throw TrexRpcException("Unable to start ZMQ server at: " + ss.str()); + } + + /* while the server is running - publish results */ + while (m_is_running) { + Json::Value snapshot; + Json::FastWriter writer; + + /* if lock was provided - take it */ + if (m_lock) { + m_lock->lock(); + } + + /* trigger a full update for stats */ + TrexStateless::get_instance().update_stats(); + + /* done with the lock */ + if (m_lock) { + m_lock->unlock(); + } + + /* encode them to JSON */ + TrexStateless::get_instance().encode_stats(snapshot); + + /* write to string and publish */ + std::string snapshot_str = writer.write(snapshot); + + zmq_send(m_socket, snapshot_str.c_str(), snapshot_str.size(), 0); + //std::cout << "sending " << snapshot_str << "\n"; + + /* relax for some time */ + std::this_thread::sleep_for (std::chrono::milliseconds(1000)); + } + + /* must be closed from the same thread */ + zmq_close(m_socket); +} + +void +TrexRpcServerAsync::_stop_rpc_thread() { + zmq_term(m_context); +} diff --git a/src/rpc-server/trex_rpc_async_server.h b/src/rpc-server/trex_rpc_async_server.h new file mode 100644 index 00000000..02d1490e --- /dev/null +++ b/src/rpc-server/trex_rpc_async_server.h @@ -0,0 +1,54 @@ +/* + Itay Marom + Cisco Systems, Inc. +*/ + +/* +Copyright (c) 2015-2015 Cisco Systems, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef __TREX_RPC_ASYNC_SERVER_H__ +#define __TREX_RPC_ASYNC_SERVER_H__ + +#include <trex_rpc_server_api.h> +#include <trex_stateless_port.h> + +/** + * async RPC server + * + * @author imarom (11-Aug-15) + */ +class TrexRpcServerAsync : public TrexRpcServerInterface { +public: + + TrexRpcServerAsync(const TrexRpcServerConfig &cfg, std::mutex *lock = NULL); + +protected: + void _rpc_thread_cb(); + void _stop_rpc_thread(); + +private: + + void handle_server_error(const std::string &specific_err); + + static const int RPC_MAX_MSG_SIZE = (20 * 1024); + void *m_context; + void *m_socket; + uint8_t m_msg_buffer[RPC_MAX_MSG_SIZE]; +}; + + +#endif /* __TREX_RPC_ASYNC_SERVER_H__ */ + diff --git a/src/rpc-server/trex_rpc_cmd.cpp b/src/rpc-server/trex_rpc_cmd.cpp index 6c355e70..920a8d30 100644 --- a/src/rpc-server/trex_rpc_cmd.cpp +++ b/src/rpc-server/trex_rpc_cmd.cpp @@ -20,7 +20,8 @@ limitations under the License. */ #include <trex_rpc_cmd_api.h> #include <trex_rpc_server_api.h> -#include <trex_stateless_api.h> +#include <trex_stateless.h> +#include <trex_stateless_port.h> trex_rpc_cmd_rc_e TrexRpcCommand::run(const Json::Value ¶ms, Json::Value &result) { diff --git a/src/rpc-server/trex_rpc_req_resp_server.cpp b/src/rpc-server/trex_rpc_req_resp_server.cpp index 3d52686c..9147f75d 100644 --- a/src/rpc-server/trex_rpc_req_resp_server.cpp +++ b/src/rpc-server/trex_rpc_req_resp_server.cpp @@ -34,7 +34,7 @@ limitations under the License. * ZMQ based request-response server * */ -TrexRpcServerReqRes::TrexRpcServerReqRes(const TrexRpcServerConfig &cfg) : TrexRpcServerInterface(cfg, "req resp") { +TrexRpcServerReqRes::TrexRpcServerReqRes(const TrexRpcServerConfig &cfg, std::mutex *lock) : TrexRpcServerInterface(cfg, "req resp", lock) { /* ZMQ is not thread safe - this should be outside */ m_context = zmq_ctx_new(); } @@ -127,6 +127,11 @@ void TrexRpcServerReqRes::handle_request(const std::string &request) { int index = 0; + /* if lock was provided, take it */ + if (m_lock) { + m_lock->lock(); + } + /* for every command parsed - launch it */ for (auto command : commands) { Json::Value single_response; @@ -138,6 +143,11 @@ void TrexRpcServerReqRes::handle_request(const std::string &request) { } + /* done with the lock */ + if (m_lock) { + m_lock->unlock(); + } + /* write the JSON to string and sever on ZMQ */ std::string response_str; diff --git a/src/rpc-server/trex_rpc_req_resp_server.h b/src/rpc-server/trex_rpc_req_resp_server.h index 7c1d66d1..1f638adf 100644 --- a/src/rpc-server/trex_rpc_req_resp_server.h +++ b/src/rpc-server/trex_rpc_req_resp_server.h @@ -32,7 +32,7 @@ limitations under the License. class TrexRpcServerReqRes : public TrexRpcServerInterface { public: - TrexRpcServerReqRes(const TrexRpcServerConfig &cfg); + TrexRpcServerReqRes(const TrexRpcServerConfig &cfg, std::mutex *lock = NULL); protected: void _rpc_thread_cb(); diff --git a/src/rpc-server/trex_rpc_server.cpp b/src/rpc-server/trex_rpc_server.cpp index 6b8c200d..a14e6f97 100644 --- a/src/rpc-server/trex_rpc_server.cpp +++ b/src/rpc-server/trex_rpc_server.cpp @@ -21,6 +21,7 @@ limitations under the License. #include <trex_rpc_server_api.h> #include <trex_rpc_req_resp_server.h> +#include <trex_rpc_async_server.h> #include <trex_rpc_jsonrpc_v2_parser.h> #include <unistd.h> #include <zmq.h> @@ -29,7 +30,7 @@ limitations under the License. /************** RPC server interface ***************/ -TrexRpcServerInterface::TrexRpcServerInterface(const TrexRpcServerConfig &cfg, const std::string &name) : m_cfg(cfg), m_name(name) { +TrexRpcServerInterface::TrexRpcServerInterface(const TrexRpcServerConfig &cfg, const std::string &name, std::mutex *lock) : m_cfg(cfg), m_name(name), m_lock(lock) { m_is_running = false; m_is_verbose = false; } @@ -112,10 +113,19 @@ get_current_date_time() { const std::string TrexRpcServer::s_server_uptime = get_current_date_time(); -TrexRpcServer::TrexRpcServer(const TrexRpcServerConfig &req_resp_cfg) { +TrexRpcServer::TrexRpcServer(const TrexRpcServerConfig *req_resp_cfg, + const TrexRpcServerConfig *async_cfg, + std::mutex *lock) { /* add the request response server */ - m_servers.push_back(new TrexRpcServerReqRes(req_resp_cfg)); + if (req_resp_cfg) { + m_servers.push_back(new TrexRpcServerReqRes(*req_resp_cfg, lock)); + } + + /* add async publisher */ + if (async_cfg) { + m_servers.push_back(new TrexRpcServerAsync(*async_cfg, lock)); + } } TrexRpcServer::~TrexRpcServer() { diff --git a/src/rpc-server/trex_rpc_server_api.h b/src/rpc-server/trex_rpc_server_api.h index 06bbe10c..ff876ac4 100644 --- a/src/rpc-server/trex_rpc_server_api.h +++ b/src/rpc-server/trex_rpc_server_api.h @@ -24,6 +24,7 @@ limitations under the License. #include <stdint.h> #include <vector> +#include <mutex> #include <thread> #include <string> #include <stdexcept> @@ -68,7 +69,7 @@ private: class TrexRpcServerInterface { public: - TrexRpcServerInterface(const TrexRpcServerConfig &cfg, const std::string &name); + TrexRpcServerInterface(const TrexRpcServerConfig &cfg, const std::string &name, std::mutex *m_lock = NULL); virtual ~TrexRpcServerInterface(); /** @@ -127,6 +128,7 @@ protected: bool m_is_verbose; std::thread *m_thread; std::string m_name; + std::mutex *m_lock; }; /** @@ -139,8 +141,11 @@ protected: class TrexRpcServer { public: - /* currently only request response server config is required */ - TrexRpcServer(const TrexRpcServerConfig &req_resp_cfg); + /* creates the collection of servers using configurations */ + TrexRpcServer(const TrexRpcServerConfig *req_resp_cfg, + const TrexRpcServerConfig *async_cfg, + std::mutex *m_lock = NULL); + ~TrexRpcServer(); /** diff --git a/src/stateless/cp/trex_stateless.cpp b/src/stateless/cp/trex_stateless.cpp new file mode 100644 index 00000000..72762e26 --- /dev/null +++ b/src/stateless/cp/trex_stateless.cpp @@ -0,0 +1,212 @@ +/* + 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_stateless.h> +#include <trex_stateless_port.h> + +#include <sched.h> +#include <iostream> +#include <unistd.h> + +using namespace std; + +/*********************************************************** + * Trex stateless object + * + **********************************************************/ +TrexStateless::TrexStateless() { + m_is_configured = false; +} + + +/** + * configure the singleton stateless object + * + */ +void TrexStateless::configure(const TrexStatelessCfg &cfg) { + + TrexStateless& instance = get_instance_internal(); + + /* check status */ + if (instance.m_is_configured) { + throw TrexException("re-configuration of stateless object is not allowed"); + } + + /* create RPC servers */ + + /* set both servers to mutex each other */ + instance.m_rpc_server = new TrexRpcServer(cfg.m_rpc_req_resp_cfg, cfg.m_rpc_async_cfg, &instance.m_global_cp_lock); + instance.m_rpc_server->set_verbose(cfg.m_rpc_server_verbose); + + /* configure ports */ + + instance.m_port_count = cfg.m_port_count; + + for (int i = 0; i < instance.m_port_count; i++) { + instance.m_ports.push_back(new TrexStatelessPort(i)); + } + + /* cores */ + instance.m_dp_core_count = cfg.m_dp_core_count; + for (int i = 0; i < instance.m_dp_core_count; i++) { + instance.m_dp_cores.push_back(new TrexStatelessDpCore(i)); + } + + /* done */ + instance.m_is_configured = true; +} + +/** + * starts the control plane side + * + */ +void +TrexStateless::launch_control_plane() { + //std::cout << "\n on control/master core \n"; + + /* pin this process to the current running CPU + any new thread will be called on the same CPU + (control plane restriction) + */ + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(sched_getcpu(), &mask); + sched_setaffinity(0, sizeof(mask), &mask); + + /* start RPC server */ + m_rpc_server->start(); +} + +void +TrexStateless::launch_on_dp_core(uint8_t core_id) { + m_dp_cores[core_id - 1]->run(); +} + +/** + * destroy the singleton and release all memory + * + * @author imarom (08-Oct-15) + */ +void +TrexStateless::destroy() { + TrexStateless& instance = get_instance_internal(); + + if (!instance.m_is_configured) { + return; + } + + /* release memory for ports */ + for (auto port : instance.m_ports) { + delete port; + } + instance.m_ports.clear(); + + /* stops the RPC server */ + instance.m_rpc_server->stop(); + delete instance.m_rpc_server; + + instance.m_rpc_server = NULL; + + /* done */ + instance.m_is_configured = false; +} + +/** + * fetch a port by ID + * + */ +TrexStatelessPort * TrexStateless::get_port_by_id(uint8_t port_id) { + if (port_id >= m_port_count) { + throw TrexException("index out of range"); + } + + return m_ports[port_id]; + +} + +uint8_t +TrexStateless::get_port_count() { + return m_port_count; +} + +uint8_t +TrexStateless::get_dp_core_count() { + return m_dp_core_count; +} + +void +TrexStateless::update_stats() { + + /* update CPU util. + TODO + */ + m_stats.m_stats.m_cpu_util = 0; + + /* for every port update and accumulate */ + for (uint8_t i = 0; i < m_port_count; i++) { + m_ports[i]->update_stats(); + + const TrexPortStats & port_stats = m_ports[i]->get_stats(); + + m_stats.m_stats.m_tx_bps += port_stats.m_stats.m_tx_bps; + m_stats.m_stats.m_rx_bps += port_stats.m_stats.m_rx_bps; + + m_stats.m_stats.m_tx_pps += port_stats.m_stats.m_tx_pps; + m_stats.m_stats.m_rx_pps += port_stats.m_stats.m_rx_pps; + + m_stats.m_stats.m_total_tx_pkts += port_stats.m_stats.m_total_tx_pkts; + m_stats.m_stats.m_total_rx_pkts += port_stats.m_stats.m_total_rx_pkts; + + m_stats.m_stats.m_total_tx_bytes += port_stats.m_stats.m_total_tx_bytes; + m_stats.m_stats.m_total_rx_bytes += port_stats.m_stats.m_total_rx_bytes; + + m_stats.m_stats.m_tx_rx_errors += port_stats.m_stats.m_tx_rx_errors; + } +} + +void +TrexStateless::encode_stats(Json::Value &global) { + + global["cpu_util"] = m_stats.m_stats.m_cpu_util; + + global["tx_bps"] = m_stats.m_stats.m_tx_bps; + global["rx_bps"] = m_stats.m_stats.m_rx_bps; + + global["tx_pps"] = m_stats.m_stats.m_tx_pps; + global["rx_pps"] = m_stats.m_stats.m_rx_pps; + + global["total_tx_pkts"] = Json::Value::UInt64(m_stats.m_stats.m_total_tx_pkts); + global["total_rx_pkts"] = Json::Value::UInt64(m_stats.m_stats.m_total_rx_pkts); + + global["total_tx_bytes"] = Json::Value::UInt64(m_stats.m_stats.m_total_tx_bytes); + global["total_rx_bytes"] = Json::Value::UInt64(m_stats.m_stats.m_total_rx_bytes); + + global["tx_rx_errors"] = Json::Value::UInt64(m_stats.m_stats.m_tx_rx_errors); + + for (uint8_t i = 0; i < m_port_count; i++) { + std::stringstream ss; + + ss << "port " << i; + Json::Value &port_section = global[ss.str()]; + + m_ports[i]->encode_stats(port_section); + } +} + diff --git a/src/stateless/cp/trex_stateless.h b/src/stateless/cp/trex_stateless.h new file mode 100644 index 00000000..649b25dd --- /dev/null +++ b/src/stateless/cp/trex_stateless.h @@ -0,0 +1,202 @@ +/* + 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_STATELESS_H__ +#define __TREX_STATELESS_H__ + +#include <stdint.h> +#include <string> +#include <stdexcept> + +#include <mutex> + +#include <trex_stream.h> +#include <trex_stateless_port.h> +#include <trex_stateless_dp_core.h> +#include <trex_rpc_server_api.h> + +/** + * generic exception for errors + * TODO: move this to a better place + */ +class TrexException : public std::runtime_error +{ +public: + TrexException() : std::runtime_error("") { + + } + TrexException(const std::string &what) : std::runtime_error(what) { + } +}; + +class TrexStatelessPort; + +/** + * unified stats + * + * @author imarom (06-Oct-15) + */ +class TrexStatelessStats { +public: + TrexStatelessStats() { + m_stats = {0}; + } + + struct { + double m_cpu_util; + + double m_tx_bps; + double m_rx_bps; + + double m_tx_pps; + double m_rx_pps; + + uint64_t m_total_tx_pkts; + uint64_t m_total_rx_pkts; + + uint64_t m_total_tx_bytes; + uint64_t m_total_rx_bytes; + + uint64_t m_tx_rx_errors; + } m_stats; +}; + +/** + * config object for stateless object + * + * @author imarom (08-Oct-15) + */ +class TrexStatelessCfg { +public: + /* default values */ + TrexStatelessCfg() { + m_port_count = 0; + m_dp_core_count = 0; + m_rpc_req_resp_cfg = NULL; + m_rpc_async_cfg = NULL; + m_rpc_server_verbose = false; + } + + const TrexRpcServerConfig *m_rpc_req_resp_cfg; + const TrexRpcServerConfig *m_rpc_async_cfg; + bool m_rpc_server_verbose; + uint8_t m_port_count; + uint8_t m_dp_core_count; +}; + +/** + * defines the T-Rex stateless operation mode + * + */ +class TrexStateless { +public: + + /** + * configure the stateless object singelton + * reconfiguration is not allowed + * an exception will be thrown + */ + static void configure(const TrexStatelessCfg &cfg); + + /** + * destroy the instance + * + */ + static void destroy(); + + /** + * singleton public get instance + * + */ + static TrexStateless& get_instance() { + TrexStateless& instance = get_instance_internal(); + + if (!instance.m_is_configured) { + throw TrexException("object is not configured"); + } + + return instance; + } + + /** + * starts the control plane side + * + */ + void launch_control_plane(); + + /** + * launch on a single DP core + * + */ + void launch_on_dp_core(uint8_t core_id); + + TrexStatelessPort * get_port_by_id(uint8_t port_id); + uint8_t get_port_count(); + + uint8_t get_dp_core_count(); + + /** + * update all the stats (deep update) + * (include all the ports and global stats) + * + */ + void update_stats(); + + /** + * fetch all the stats + * + */ + void encode_stats(Json::Value &global); + + +protected: + TrexStateless(); + + static TrexStateless& get_instance_internal () { + static TrexStateless instance; + return instance; + } + + /* c++ 2011 style singleton */ + TrexStateless(TrexStateless const&) = delete; + void operator=(TrexStateless const&) = delete; + + /* status */ + bool m_is_configured; + + /* RPC server array */ + TrexRpcServer *m_rpc_server; + + /* ports */ + std::vector <TrexStatelessPort *> m_ports; + uint8_t m_port_count; + + /* cores */ + std::vector <TrexStatelessDpCore *> m_dp_cores; + uint8_t m_dp_core_count; + + /* stats */ + TrexStatelessStats m_stats; + + std::mutex m_global_cp_lock; +}; + +#endif /* __TREX_STATELESS_H__ */ + diff --git a/src/stateless/cp/trex_stateless_port.cpp b/src/stateless/cp/trex_stateless_port.cpp new file mode 100644 index 00000000..a31847a5 --- /dev/null +++ b/src/stateless/cp/trex_stateless_port.cpp @@ -0,0 +1,228 @@ +/* + 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_stateless.h> +#include <trex_stateless_port.h> +#include <string> + +#ifndef TREX_RPC_MOCK_SERVER +// DPDK c++ issue +#define UINT8_MAX 255 +#define UINT16_MAX 0xFFFF +// DPDK c++ issue +#endif + +#include <rte_ethdev.h> +#include <os_time.h> + +using namespace std; + +/*************************** + * trex stateless port + * + **************************/ +TrexStatelessPort::TrexStatelessPort(uint8_t port_id) : m_port_id(port_id) { + m_port_state = PORT_STATE_UP_IDLE; + clear_owner(); +} + + +/** + * starts the traffic on the port + * + */ +TrexStatelessPort::rc_e +TrexStatelessPort::start_traffic(void) { + + if (m_port_state != PORT_STATE_UP_IDLE) { + return (RC_ERR_BAD_STATE_FOR_OP); + } + + if (get_stream_table()->size() == 0) { + return (RC_ERR_NO_STREAMS); + } + + m_port_state = PORT_STATE_TRANSMITTING; + + /* real code goes here */ + return (RC_OK); +} + +void +TrexStatelessPort::stop_traffic(void) { + + /* real code goes here */ + if (m_port_state == PORT_STATE_TRANSMITTING) { + m_port_state = PORT_STATE_UP_IDLE; + } +} + +/** +* access the stream table +* +*/ +TrexStreamTable * TrexStatelessPort::get_stream_table() { + return &m_stream_table; +} + + +std::string +TrexStatelessPort::get_state_as_string() { + + switch (get_state()) { + case PORT_STATE_DOWN: + return "down"; + + case PORT_STATE_UP_IDLE: + return "idle"; + + case PORT_STATE_TRANSMITTING: + return "transmitting"; + } + + return "unknown"; +} + +void +TrexStatelessPort::get_properties(string &driver, string &speed) { + + /* take this from DPDK */ + driver = "e1000"; + speed = "1 Gbps"; +} + + +/** + * generate a random connection handler + * + */ +std::string +TrexStatelessPort::generate_handler() { + std::stringstream ss; + + static const char alphanum[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + /* generate 8 bytes of random handler */ + for (int i = 0; i < 8; ++i) { + ss << alphanum[rand() % (sizeof(alphanum) - 1)]; + } + + return (ss.str()); +} + +/** + * update stats for the port + * + */ +void +TrexStatelessPort::update_stats() { + struct rte_eth_stats stats; + rte_eth_stats_get(m_port_id, &stats); + + /* copy straight values */ + m_stats.m_stats.m_total_tx_bytes = stats.obytes; + m_stats.m_stats.m_total_rx_bytes = stats.ibytes; + + m_stats.m_stats.m_total_tx_pkts = stats.opackets; + m_stats.m_stats.m_total_rx_pkts = stats.ipackets; + + /* calculate stats */ + m_stats.m_stats.m_tx_bps = m_stats.m_bw_tx_bps.add(stats.obytes); + m_stats.m_stats.m_rx_bps = m_stats.m_bw_rx_bps.add(stats.ibytes); + + m_stats.m_stats.m_tx_pps = m_stats.m_bw_tx_pps.add(stats.opackets); + m_stats.m_stats.m_rx_pps = m_stats.m_bw_rx_pps.add(stats.ipackets); + +} + +const TrexPortStats & +TrexStatelessPort::get_stats() { + return m_stats; +} + +void +TrexStatelessPort::encode_stats(Json::Value &port) { + + port["tx_bps"] = m_stats.m_stats.m_tx_bps; + port["rx_bps"] = m_stats.m_stats.m_rx_bps; + + port["tx_pps"] = m_stats.m_stats.m_tx_pps; + port["rx_pps"] = m_stats.m_stats.m_rx_pps; + + port["total_tx_pkts"] = Json::Value::UInt64(m_stats.m_stats.m_total_tx_pkts); + port["total_rx_pkts"] = Json::Value::UInt64(m_stats.m_stats.m_total_rx_pkts); + + port["total_tx_bytes"] = Json::Value::UInt64(m_stats.m_stats.m_total_tx_bytes); + port["total_rx_bytes"] = Json::Value::UInt64(m_stats.m_stats.m_total_rx_bytes); + + port["tx_rx_errors"] = Json::Value::UInt64(m_stats.m_stats.m_tx_rx_errors); +} + + + +/*************************** + * BW measurement + * + **************************/ +/* TODO: move this to a common place */ +BWMeasure::BWMeasure() { + reset(); +} + +void BWMeasure::reset(void) { + m_start=false; + m_last_time_msec=0; + m_last_bytes=0; + m_last_result=0.0; +}; + +double BWMeasure::calc_MBsec(uint32_t dtime_msec, + uint64_t dbytes){ + double rate=0.000008*( ( (double)dbytes*(double)os_get_time_freq())/((double)dtime_msec) ); + return(rate); +} + +double BWMeasure::add(uint64_t size) { + if ( false == m_start ) { + m_start=true; + m_last_time_msec = os_get_time_msec() ; + m_last_bytes=size; + return(0.0); + } + + uint32_t ctime=os_get_time_msec(); + if ((ctime - m_last_time_msec) <os_get_time_freq() ) { + return(m_last_result); + } + + uint32_t dtime_msec = ctime-m_last_time_msec; + uint64_t dbytes = size - m_last_bytes; + + m_last_time_msec = ctime; + m_last_bytes = size; + + m_last_result= 0.5*calc_MBsec(dtime_msec,dbytes) +0.5*(m_last_result); + return( m_last_result ); +} + + diff --git a/src/stateless/trex_stateless_api.h b/src/stateless/cp/trex_stateless_port.h index 7a9080aa..428d5aee 100644 --- a/src/stateless/trex_stateless_api.h +++ b/src/stateless/cp/trex_stateless_port.h @@ -18,27 +18,74 @@ 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_STATELESS_API_H__ -#define __TREX_STATELESS_API_H__ +#ifndef __TREX_STATELESS_PORT_H__ +#define __TREX_STATELESS_PORT_H__ -#include <stdint.h> -#include <string> -#include <stdexcept> +#include <trex_stream.h> -#include <trex_stream_api.h> +/** + * bandwidth measurement class + * + */ +class BWMeasure { +public: + BWMeasure(); + void reset(void); + double add(uint64_t size); + +private: + double calc_MBsec(uint32_t dtime_msec, + uint64_t dbytes); + +public: + bool m_start; + uint32_t m_last_time_msec; + uint64_t m_last_bytes; + double m_last_result; +}; /** - * generic exception for errors - * TODO: move this to a better place + * TRex stateless port stats + * + * @author imarom (24-Sep-15) */ -class TrexException : public std::runtime_error -{ +class TrexPortStats { + public: - TrexException() : std::runtime_error("") { + TrexPortStats() { + m_stats = {0}; + m_bw_tx_bps.reset(); + m_bw_rx_bps.reset(); + + m_bw_tx_pps.reset(); + m_bw_rx_pps.reset(); } - TrexException(const std::string &what) : std::runtime_error(what) { - } + +public: + + BWMeasure m_bw_tx_bps; + BWMeasure m_bw_rx_bps; + + BWMeasure m_bw_tx_pps; + BWMeasure m_bw_rx_pps; + + struct { + + double m_tx_bps; + double m_rx_bps; + + double m_tx_pps; + double m_rx_pps; + + uint64_t m_total_tx_pkts; + uint64_t m_total_rx_pkts; + + uint64_t m_total_tx_bytes; + uint64_t m_total_rx_bytes; + + uint64_t m_tx_rx_errors; + } m_stats; }; /** @@ -49,20 +96,6 @@ public: class TrexStatelessPort { public: - struct TrexPortStats { - uint64_t tx_pps; - uint64_t tx_bps; - uint64_t total_tx_pkts; - uint64_t total_tx_bytes; - - uint64_t rx_pps; - uint64_t rx_bps; - uint64_t total_rx_pkts; - uint64_t total_rx_bytes; - - uint64_t tx_rx_errors; - }; - /** * port state */ @@ -169,15 +202,18 @@ public: } - const TrexPortStats & get_port_stats(void) { - /* scrabble */ - m_stats.tx_bps += 1 + rand() % 100; - m_stats.tx_pps += 1 + rand() % 10; - m_stats.total_tx_bytes += 1 + rand() % 10; - m_stats.total_tx_pkts += 1 + rand() % 5; + /** + * update the values of the stats + * + */ + void update_stats(); + + const TrexPortStats & get_stats(); - return m_stats; - } + /** + * encode stats as JSON + */ + void encode_stats(Json::Value &port); private: @@ -191,54 +227,4 @@ private: TrexPortStats m_stats; }; -/** - * defines the T-Rex stateless operation mode - * - */ -class TrexStateless { -public: - - /** - * configure the stateless object singelton - * reconfiguration is not allowed - * an exception will be thrown - */ - static void configure(uint8_t port_count); - - /** - * singleton public get instance - * - */ - static TrexStateless& get_instance() { - TrexStateless& instance = get_instance_internal(); - - if (!instance.m_is_configured) { - throw TrexException("object is not configured"); - } - - return instance; - } - - TrexStatelessPort *get_port_by_id(uint8_t port_id); - uint8_t get_port_count(); - -protected: - TrexStateless(); - ~TrexStateless(); - - static TrexStateless& get_instance_internal () { - static TrexStateless instance; - return instance; - } - - /* c++ 2011 style singleton */ - TrexStateless(TrexStateless const&) = delete; - void operator=(TrexStateless const&) = delete; - - bool m_is_configured; - TrexStatelessPort **m_ports; - uint8_t m_port_count; -}; - -#endif /* __TREX_STATELESS_API_H__ */ - +#endif /* __TREX_STATELESS_PORT_H__ */ diff --git a/src/stateless/trex_stream.cpp b/src/stateless/cp/trex_stream.cpp index 8bf04748..182036f1 100644 --- a/src/stateless/trex_stream.cpp +++ b/src/stateless/cp/trex_stream.cpp @@ -18,7 +18,7 @@ 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_stream_api.h> +#include <trex_stream.h> #include <cstddef> /************************************** diff --git a/src/stateless/trex_stream_api.h b/src/stateless/cp/trex_stream.h index d3c0fb29..f5bc96ef 100644 --- a/src/stateless/trex_stream_api.h +++ b/src/stateless/cp/trex_stream.h @@ -18,8 +18,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -#ifndef __TREX_STREAM_API_H__ -#define __TREX_STREAM_API_H__ +#ifndef __TREX_STREAM_H__ +#define __TREX_STREAM_H__ #include <unordered_map> #include <vector> @@ -205,5 +205,5 @@ private: std::unordered_map<int, TrexStream *> m_stream_table; }; -#endif /* __TREX_STREAM_API_H__ */ +#endif /* __TREX_STREAM_H__ */ diff --git a/src/stateless/trex_stream_vm.cpp b/src/stateless/cp/trex_stream_vm.cpp index 2e760ae9..2e760ae9 100644 --- a/src/stateless/trex_stream_vm.cpp +++ b/src/stateless/cp/trex_stream_vm.cpp diff --git a/src/stateless/trex_stream_vm.h b/src/stateless/cp/trex_stream_vm.h index 56edbcaf..56edbcaf 100644 --- a/src/stateless/trex_stream_vm.h +++ b/src/stateless/cp/trex_stream_vm.h diff --git a/src/stateless/dp/trex_stateless_dp_core.cpp b/src/stateless/dp/trex_stateless_dp_core.cpp new file mode 100644 index 00000000..3755b82c --- /dev/null +++ b/src/stateless/dp/trex_stateless_dp_core.cpp @@ -0,0 +1,135 @@ +/* + 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_stateless_dp_core.h> +#include <stdio.h> +#include <unistd.h> +#include <trex_stateless.h> + +#include <bp_sim.h> + +#ifndef TREX_RPC_MOCK_SERVER + +// DPDK c++ issue +#define UINT8_MAX 255 +#define UINT16_MAX 0xFFFF +// DPDK c++ issue +#endif + +#include <rte_ethdev.h> +#include "mbuf.h" + +/** + * TEST + * + */ +static const uint8_t udp_pkt[]={ + 0x00,0x00,0x00,0x01,0x00,0x00, + 0x00,0x00,0x00,0x01,0x00,0x00, + 0x08,0x00, + + 0x45,0x00,0x00,0x81, + 0xaf,0x7e,0x00,0x00, + 0x12,0x11,0xd9,0x23, + 0x01,0x01,0x01,0x01, + 0x3d,0xad,0x72,0x1b, + + 0x11,0x11, + 0x11,0x11, + + 0x00,0x6d, + 0x00,0x00, + + 0x64,0x31,0x3a,0x61, + 0x64,0x32,0x3a,0x69,0x64, + 0x32,0x30,0x3a,0xd0,0x0e, + 0xa1,0x4b,0x7b,0xbd,0xbd, + 0x16,0xc6,0xdb,0xc4,0xbb,0x43, + 0xf9,0x4b,0x51,0x68,0x33,0x72, + 0x20,0x39,0x3a,0x69,0x6e,0x66,0x6f, + 0x5f,0x68,0x61,0x73,0x68,0x32,0x30,0x3a,0xee,0xc6,0xa3, + 0xd3,0x13,0xa8,0x43,0x06,0x03,0xd8,0x9e,0x3f,0x67,0x6f, + 0xe7,0x0a,0xfd,0x18,0x13,0x8d,0x65,0x31,0x3a,0x71,0x39, + 0x3a,0x67,0x65,0x74,0x5f,0x70,0x65,0x65,0x72,0x73,0x31, + 0x3a,0x74,0x38,0x3a,0x3d,0xeb,0x0c,0xbf,0x0d,0x6a,0x0d, + 0xa5,0x31,0x3a,0x79,0x31,0x3a,0x71,0x65,0x87,0xa6,0x7d, + 0xe7 +}; + +static int +test_inject_pkt(uint8_t *pkt, uint32_t pkt_size) { + + #ifndef TREX_RPC_MOCK_SERVER + rte_mempool_t * mp= CGlobalInfo::m_mem_pool[0].m_big_mbuf_pool ; + #else + rte_mempool_t * mp = NULL; + #endif + + rte_mbuf_t *m = rte_pktmbuf_alloc(mp); + if ( unlikely(m==0) ) { + printf("ERROR no packets \n"); + return (-1); + } + char *p = rte_pktmbuf_append(m, pkt_size); + assert(p); + /* set pkt data */ + memcpy(p,pkt,pkt_size); + + rte_mbuf_t *tx_pkts[32]; + tx_pkts[0] = m; + uint8_t nb_pkts = 1; + uint16_t ret = rte_eth_tx_burst(0, 0, tx_pkts, nb_pkts); + (void)ret; + rte_pktmbuf_free(m); + + return (0); +} + +static int +test_inject_udp_pkt(){ + return (test_inject_pkt((uint8_t*)udp_pkt,sizeof(udp_pkt))); +} + +void +TrexStatelessDpCore::test_inject_dummy_pkt() { + test_inject_udp_pkt(); +} + +/*************************** + * DP core + * + **************************/ +TrexStatelessDpCore::TrexStatelessDpCore(uint8_t core_id) : m_core_id(core_id) { +} + +/** + * main function for DP core + * + */ +void +TrexStatelessDpCore::run() { + printf("\nOn DP core %d\n", m_core_id); + while (true) { + test_inject_dummy_pkt(); + rte_pause(); + } +} + diff --git a/src/stateless/dp/trex_stateless_dp_core.h b/src/stateless/dp/trex_stateless_dp_core.h new file mode 100644 index 00000000..4b09b752 --- /dev/null +++ b/src/stateless/dp/trex_stateless_dp_core.h @@ -0,0 +1,43 @@ +/* + 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_STATELESS_DP_CORE_H__ +#define __TREX_STATELESS_DP_CORE_H__ + +#include <stdint.h> + +/** + * stateless DP core object + * + */ +class TrexStatelessDpCore { +public: + + TrexStatelessDpCore(uint8_t core_id); + + /* starts the DP core run */ + void run(); + +private: + void test_inject_dummy_pkt(); + uint8_t m_core_id; +}; + +#endif /* __TREX_STATELESS_DP_CORE_H__ */ diff --git a/src/stateless/trex_stateless.cpp b/src/stateless/trex_stateless.cpp deleted file mode 100644 index 0eb96f05..00000000 --- a/src/stateless/trex_stateless.cpp +++ /dev/null @@ -1,171 +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_stateless_api.h> - -using namespace std; - -/*********************************************************** - * Trex stateless object - * - **********************************************************/ -TrexStateless::TrexStateless() { - m_is_configured = false; -} - -/** - * one time configuration of the stateless object - * - */ -void TrexStateless::configure(uint8_t port_count) { - - TrexStateless& instance = get_instance_internal(); - - if (instance.m_is_configured) { - throw TrexException("re-configuration of stateless object is not allowed"); - } - - instance.m_port_count = port_count; - instance.m_ports = new TrexStatelessPort*[port_count]; - - for (int i = 0; i < instance.m_port_count; i++) { - instance.m_ports[i] = new TrexStatelessPort(i); - } - - instance.m_is_configured = true; -} - -TrexStateless::~TrexStateless() { - for (int i = 0; i < m_port_count; i++) { - delete m_ports[i]; - } - - delete [] m_ports; -} - -TrexStatelessPort * TrexStateless::get_port_by_id(uint8_t port_id) { - if (port_id >= m_port_count) { - throw TrexException("index out of range"); - } - - return m_ports[port_id]; - -} - -uint8_t TrexStateless::get_port_count() { - return m_port_count; -} - -/*************************** - * trex stateless port - * - **************************/ -TrexStatelessPort::TrexStatelessPort(uint8_t port_id) : m_port_id(port_id) { - m_port_state = PORT_STATE_UP_IDLE; - clear_owner(); - m_stats = {0}; -} - - -/** - * starts the traffic on the port - * - */ -TrexStatelessPort::rc_e -TrexStatelessPort::start_traffic(void) { - - if (m_port_state != PORT_STATE_UP_IDLE) { - return (RC_ERR_BAD_STATE_FOR_OP); - } - - if (get_stream_table()->size() == 0) { - return (RC_ERR_NO_STREAMS); - } - - m_port_state = PORT_STATE_TRANSMITTING; - - /* real code goes here */ - return (RC_OK); -} - -void -TrexStatelessPort::stop_traffic(void) { - - /* real code goes here */ - if (m_port_state == PORT_STATE_TRANSMITTING) { - m_port_state = PORT_STATE_UP_IDLE; - } -} - -/** -* access the stream table -* -*/ -TrexStreamTable * TrexStatelessPort::get_stream_table() { - return &m_stream_table; -} - - -std::string -TrexStatelessPort::get_state_as_string() { - - switch (get_state()) { - case PORT_STATE_DOWN: - return "down"; - - case PORT_STATE_UP_IDLE: - return "idle"; - - case PORT_STATE_TRANSMITTING: - return "transmitting"; - } - - return "unknown"; -} - -void -TrexStatelessPort::get_properties(string &driver, string &speed) { - - /* take this from DPDK */ - driver = "e1000"; - speed = "1 Gbps"; -} - - -/** - * generate a random connection handler - * - */ -std::string -TrexStatelessPort::generate_handler() { - std::stringstream ss; - - static const char alphanum[] = - "0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz"; - - /* generate 8 bytes of random handler */ - for (int i = 0; i < 8; ++i) { - ss << alphanum[rand() % (sizeof(alphanum) - 1)]; - } - - return (ss.str()); -} diff --git a/src/tuple_gen.cpp b/src/tuple_gen.cpp index e408f275..d3538ce6 100755 --- a/src/tuple_gen.cpp +++ b/src/tuple_gen.cpp @@ -57,7 +57,7 @@ void CClientPool::Create(IP_DIST_t dist_value, uint32_t max_ip, double l_flow, double t_cps, - CFlowGenList* fl_list, + CFlowGenListMac* mac_info, bool has_mac_map, uint16_t tcp_aging, uint16_t udp_aging) { @@ -65,10 +65,10 @@ void CClientPool::Create(IP_DIST_t dist_value, set_dist(dist_value); uint32_t total_ip = max_ip - min_ip +1; uint32_t avail_ip = total_ip; - if (has_mac_map && (fl_list!=NULL)) { + if (has_mac_map && (mac_info!=NULL)) { for(int idx=0;idx<total_ip;idx++){ mac_addr_align_t *mac_adr = NULL; - mac_adr = get_mac_addr_by_ip(fl_list, min_ip+idx); + mac_adr = mac_info->get_mac_addr_by_ip(min_ip+idx); if (mac_adr == NULL) { avail_ip--; } @@ -86,7 +86,7 @@ void CClientPool::Create(IP_DIST_t dist_value, if (has_mac_map) { for(int idx=0;idx<total_ip;idx++){ mac_addr_align_t *mac_adr = NULL; - mac_adr = get_mac_addr_by_ip(fl_list, min_ip+idx); + mac_adr = mac_info->get_mac_addr_by_ip( min_ip+idx); if (mac_adr != NULL) { m_ip_info[idx] = new CClientInfoL(has_mac_map); m_ip_info[idx]->set_ip(min_ip+idx); @@ -103,7 +103,7 @@ void CClientPool::Create(IP_DIST_t dist_value, if (has_mac_map) { for(int idx=0;idx<total_ip;idx++){ mac_addr_align_t *mac_adr = NULL; - mac_adr = get_mac_addr_by_ip(fl_list, min_ip+idx); + mac_adr = mac_info->get_mac_addr_by_ip(min_ip+idx); if (mac_adr != NULL) { m_ip_info[idx] = new CClientInfo(has_mac_map); m_ip_info[idx]->set_ip(min_ip+idx); @@ -123,20 +123,19 @@ void CClientPool::Create(IP_DIST_t dist_value, CreateBase(); } -void delay(int msec); bool CTupleGeneratorSmart::add_client_pool(IP_DIST_t client_dist, uint32_t min_client, uint32_t max_client, double l_flow, double t_cps, - CFlowGenList* fl_list, + CFlowGenListMac* mac_info, uint16_t tcp_aging, uint16_t udp_aging){ assert(max_client>=min_client); CClientPool* pool = new CClientPool(); pool->Create(client_dist, min_client, max_client, - l_flow, t_cps, fl_list, has_mac_mapping, + l_flow, t_cps, mac_info, m_has_mac_mapping, tcp_aging, udp_aging); m_client_pool.push_back(pool); @@ -171,13 +170,13 @@ bool CTupleGeneratorSmart::Create(uint32_t _id, m_thread_id = thread_id; m_id = _id; m_was_init=true; - has_mac_mapping = has_mac; + m_has_mac_mapping = has_mac; return(true); } void CTupleGeneratorSmart::Delete(){ m_was_init=false; - has_mac_mapping = false; + m_has_mac_mapping = false; for (int idx=0;idx<m_client_pool.size();idx++) { m_client_pool[idx]->Delete(); @@ -224,52 +223,53 @@ bool CTupleGenPoolYaml::is_valid(uint32_t num_threads,bool is_plugins){ +#define UTL_YAML_READ(type, field, target) if (node.FindValue(#field)) { \ + utl_yaml_read_ ## type (node, #field , target); \ + } else { printf("generator definition mising " #field "\n"); } - -void operator >> (const YAML::Node& node, CTupleGenPoolYaml & fi) { +IP_DIST_t convert_distribution (const YAML::Node& node) { std::string tmp; - node["name"] >> fi.m_name; node["distribution"] >> tmp ; if (tmp == "random") { - fi.m_dist=cdRANDOM_DIST; + return cdRANDOM_DIST; }else if (tmp == "normal") { - fi.m_dist=cdNORMAL_DIST; + return cdNORMAL_DIST; } else { - fi.m_dist=cdSEQ_DIST; + return cdSEQ_DIST; } - utl_yaml_read_ip_addr(node,"ip_start",fi.m_ip_start); - utl_yaml_read_ip_addr(node,"ip_end",fi.m_ip_end); - fi.m_number_of_clients_per_gb = 0; +} +void read_tuple_para(const YAML::Node& node, CTupleGenPoolYaml & fi) { + UTL_YAML_READ(uint32, clients_per_gb, fi.m_number_of_clients_per_gb); + UTL_YAML_READ(uint32, min_clients, fi.m_min_clients); + UTL_YAML_READ(ip_addr, dual_port_mask, fi.m_dual_interface_mask); + UTL_YAML_READ(uint16, tcp_aging, fi.m_tcp_aging_sec); + UTL_YAML_READ(uint16, udp_aging, fi.m_udp_aging_sec); +} + +void operator >> (const YAML::Node& node, CTupleGenPoolYaml & fi) { + if (node.FindValue("name")) { + node["name"] >> fi.m_name; + } else { + printf("error in generator definition, name missing\n"); + assert(0); + } + if (node.FindValue("distribution")) { + fi.m_dist = convert_distribution(node); + } + UTL_YAML_READ(ip_addr, ip_start, fi.m_ip_start); + UTL_YAML_READ(ip_addr, ip_end, fi.m_ip_end); + + fi.m_number_of_clients_per_gb = 0; fi.m_min_clients = 0; fi.m_is_bundling = false; fi.m_tcp_aging_sec = 0; fi.m_udp_aging_sec = 0; fi.m_dual_interface_mask = 0; - try { - utl_yaml_read_uint32(node,"clients_per_gb",fi.m_number_of_clients_per_gb); - } catch ( const std::exception& e ) { - ;} - try { - utl_yaml_read_uint32(node,"min_clients",fi.m_min_clients); - } catch ( const std::exception& e ) { - ;} - try { - utl_yaml_read_ip_addr(node,"dual_port_mask",fi.m_dual_interface_mask); - } catch ( const std::exception& e ) { - ;} - try { - utl_yaml_read_uint16(node,"tcp_aging",fi.m_tcp_aging_sec); - } catch ( const std::exception& e ) { - ;} - try { - utl_yaml_read_uint16(node,"udp_aging",fi.m_udp_aging_sec); - } catch ( const std::exception& e ) { - ;} - try { + read_tuple_para(node, fi); + if (node.FindValue("track_ports")) { node["track_ports"] >> fi.m_is_bundling; - } catch ( const std::exception& e ) { - ;} + } } void copy_global_pool_para(CTupleGenPoolYaml & src, CTupleGenPoolYaml & dst) { if (src.m_number_of_clients_per_gb == 0) @@ -283,69 +283,54 @@ void copy_global_pool_para(CTupleGenPoolYaml & src, CTupleGenPoolYaml & dst) { if (src.m_udp_aging_sec == 0) src.m_udp_aging_sec = dst.m_udp_aging_sec; } + + void operator >> (const YAML::Node& node, CTupleGenYamlInfo & fi) { - std::string tmp; - try { - CTupleGenPoolYaml c_pool; - CTupleGenPoolYaml s_pool; - node["distribution"] >> tmp ; - if (tmp == "random") { - c_pool.m_dist=cdRANDOM_DIST; - }else if (tmp == "normal") { - c_pool.m_dist=cdNORMAL_DIST; - } else { - c_pool.m_dist=cdSEQ_DIST; - } + CTupleGenPoolYaml c_pool; + CTupleGenPoolYaml s_pool; + + if (node.FindValue("distribution")) { + c_pool.m_dist = convert_distribution(node); s_pool.m_dist = c_pool.m_dist; - utl_yaml_read_ip_addr(node,"clients_start",c_pool.m_ip_start); - utl_yaml_read_ip_addr(node,"clients_end",c_pool.m_ip_end); - utl_yaml_read_ip_addr(node,"servers_start",s_pool.m_ip_start); - utl_yaml_read_ip_addr(node,"servers_end",s_pool.m_ip_end); - utl_yaml_read_uint32(node,"clients_per_gb",c_pool.m_number_of_clients_per_gb); - utl_yaml_read_uint32(node,"min_clients",c_pool.m_min_clients); - utl_yaml_read_ip_addr(node,"dual_port_mask",c_pool.m_dual_interface_mask); - utl_yaml_read_uint16(node,"tcp_aging",c_pool.m_tcp_aging_sec); - utl_yaml_read_uint16(node,"udp_aging",c_pool.m_udp_aging_sec); + UTL_YAML_READ(ip_addr, clients_start, c_pool.m_ip_start); + UTL_YAML_READ(ip_addr, clients_end, c_pool.m_ip_end); + UTL_YAML_READ(ip_addr, servers_start, s_pool.m_ip_start); + UTL_YAML_READ(ip_addr, servers_end, s_pool.m_ip_end); + read_tuple_para(node, c_pool); s_pool.m_dual_interface_mask = c_pool.m_dual_interface_mask; s_pool.m_is_bundling = false; fi.m_client_pool.push_back(c_pool); fi.m_server_pool.push_back(s_pool); - }catch ( const std::exception& e ) { + } else { printf("No default generator defined.\n"); } - try{ + + if (node.FindValue("generator_clients")) { const YAML::Node& c_pool_info = node["generator_clients"]; for (uint16_t idx=0;idx<c_pool_info.size();idx++) { CTupleGenPoolYaml pool; - try { - c_pool_info[idx] >> pool; - if (fi.m_client_pool.size()>0) { - copy_global_pool_para(pool, fi.m_client_pool[0]); - } - fi.m_client_pool.push_back(pool); - } catch ( const std::exception& e ) { - printf("client pool in YAML is wrong\n"); + c_pool_info[idx] >> pool; + if (fi.m_client_pool.size()>0) { + copy_global_pool_para(pool, fi.m_client_pool[0]); } + fi.m_client_pool.push_back(pool); } - }catch ( const std::exception& e ) { + } else { printf("no client generator pool configured, using default pool\n"); } - try { + + if (node.FindValue("generator_servers")) { const YAML::Node& s_pool_info = node["generator_servers"]; for (uint16_t idx=0;idx<s_pool_info.size();idx++) { CTupleGenPoolYaml pool; - try { - s_pool_info[idx] >> pool; - } catch ( const std::exception& e ) { - printf("server pool in YAML is wrong\n"); - } + s_pool_info[idx] >> pool; if (fi.m_server_pool.size()>0) { copy_global_pool_para(pool, fi.m_server_pool[0]); } fi.m_server_pool.push_back(pool); } - }catch ( const std::exception& e ) { + } else { printf("no server generator pool configured, using default pool\n"); } } diff --git a/src/tuple_gen.h b/src/tuple_gen.h index fb856538..29adbd69 100755 --- a/src/tuple_gen.h +++ b/src/tuple_gen.h @@ -37,10 +37,97 @@ limitations under the License. #include "common/c_common.h" #include <bitset> #include <yaml-cpp/yaml.h> - +#include <mac_mapping.h> #include <random> +class CTupleBase { +public: + CTupleBase() { + m_client_mac.inused = UNUSED; + } + uint32_t getClient() { + return m_client_ip; + } + void setClient(uint32_t ip) { + m_client_ip = ip; + } + uint32_t getClientId() { + return m_client_idx; + } + void setClientId(uint32_t id) { + m_client_idx = id; + } + + uint32_t getServer(){ + return m_server_ip; + } + void setServer(uint32_t ip) { + m_server_ip = ip; + } + uint32_t getServerId(){ + return m_server_idx; + } + void setServerId(uint32_t id) { + m_server_idx = id; + } + uint16_t getServerPort() { + return m_server_port; + } + void setServerPort(uint16_t port) { + m_server_port = port; + } + uint16_t getClientPort() { + return m_client_port; + } + void setClientPort(uint16_t port) { + m_client_port = port; + } + mac_addr_align_t* getClientMac() { + return &m_client_mac; + } + void setClientMac(mac_addr_align_t* mac_info) { + if (mac_info != NULL) { + memcpy(&m_client_mac, mac_info, sizeof(mac_addr_align_t)); + m_client_mac.inused = INUSED; + } else { + m_client_mac.inused = UNUSED; + } + } + void setClientTuple(uint32_t ip,mac_addr_align_t*mac,uint16_t port) { + setClient(ip); + setClientMac(mac); + setClientPort(port); + } + void setClientAll2(uint32_t id, uint32_t ip,uint16_t port) { + setClientId(id); + setClient(ip); + setClientPort(port); + } + + void setServerAll(uint32_t id, uint32_t ip) { + setServerId(id); + setServer(ip); + } + void getClientAll(uint32_t & id, uint32_t & ip, uint32_t & port) { + id = getClientId(); + ip = getClient(); + port = getClientPort(); + } + void getServerAll(uint32_t & id, uint32_t & ip) { + id = getServerId(); + ip = getServer(); + } +private: + uint32_t m_client_ip; + uint32_t m_client_idx; + uint32_t m_server_ip; + uint32_t m_server_idx; + mac_addr_align_t m_client_mac; + uint16_t m_client_port; + uint16_t m_server_port; +}; + /* @@ -59,9 +146,8 @@ limitations under the License. #define FOREACH(vector) for(int i=0;i<vector.size();i++) -/* Client distribution */ - +/* Client distribution */ typedef enum { cdSEQ_DIST = 0, cdRANDOM_DIST = 1, @@ -69,22 +155,6 @@ typedef enum { cdMAX_DIST = 3 } IP_DIST_t ; -#define INUSED 0 -#define UNUSED 1 -typedef struct mac_addr_align_ { -public: - uint8_t mac[6]; - uint8_t inused; - uint8_t pad; -} mac_addr_align_t; - -typedef struct mac_mapping_ { - mac_addr_align_t mac; - uint32_t ip; -} mac_mapping_t; - - - /* For type 1, we generator port by maintaining a 64K bit array for each port. * In this case, we cannot support large number of clients due to memory exhausted. * @@ -99,13 +169,13 @@ typedef struct mac_mapping_ { #define TYPE2 1 #define MAX_TYPE 3 - class CIpInfoBase { public: virtual mac_addr_align_t* get_mac() { return NULL;} virtual void set_mac(mac_addr_align_t*){;} virtual uint16_t get_new_free_port() = 0; virtual void return_port(uint16_t a) = 0; + virtual void generate_tuple(CTupleBase & tuple) = 0; virtual void return_all_ports() = 0; uint32_t get_ip() { return m_ip; @@ -113,7 +183,8 @@ class CIpInfoBase { void set_ip(uint32_t ip) { m_ip = ip; } - public: + virtual ~CIpInfoBase() {} + protected: uint32_t m_ip; }; @@ -233,31 +304,37 @@ class CIpInfo : public CIpInfoBase { }; class CClientInfo : public CIpInfo { - public: - CClientInfo (bool has_mac) { - if (has_mac==true) { - m_mac = new mac_addr_align_t(); - } else { - m_mac = NULL; - } - } - CClientInfo () { +public: + CClientInfo (bool has_mac) { + if (has_mac==true) { + m_mac = new mac_addr_align_t(); + } else { m_mac = NULL; } - - mac_addr_align_t* get_mac() { - return m_mac; - } - void set_mac(mac_addr_align_t *mac) { - memcpy(m_mac, mac, sizeof(mac_addr_align_t)); - } - ~CClientInfo() { - if (m_mac!=NULL){ - delete m_mac; - } + } + CClientInfo () { + m_mac = NULL; + } + + mac_addr_align_t* get_mac() { + return m_mac; + } + void set_mac(mac_addr_align_t *mac) { + memcpy(m_mac, mac, sizeof(mac_addr_align_t)); + } + ~CClientInfo() { + if (m_mac!=NULL){ + delete m_mac; + m_mac=NULL; } - private: - mac_addr_align_t *m_mac; + } + + void generate_tuple(CTupleBase & tuple) { + tuple.setClientTuple(m_ip, m_mac, + get_new_free_port()); + } +private: + mac_addr_align_t *m_mac; }; class CClientInfoL : public CIpInfoL { @@ -272,100 +349,44 @@ public: CClientInfoL () { m_mac = NULL; } - + mac_addr_align_t* get_mac() { return m_mac; } + void set_mac(mac_addr_align_t *mac) { memcpy(m_mac, mac, sizeof(mac_addr_align_t)); } + ~CClientInfoL() { - if (m_mac!=NULL) { + if (m_mac!=NULL){ delete m_mac; + m_mac=NULL; } } + + void generate_tuple(CTupleBase & tuple) { + tuple.setClientTuple(m_ip, m_mac, + get_new_free_port()); + } private: mac_addr_align_t *m_mac; }; class CServerInfo : public CIpInfo { - ; + void generate_tuple(CTupleBase & tuple) { + tuple.setServer(m_ip); + } }; class CServerInfoL : public CIpInfoL { - ; -}; - - -class CTupleBase { -public: - CTupleBase() { - m_client_mac.inused = UNUSED; - } - uint32_t getClient() { - return m_client_ip; - } - void setClient(uint32_t ip) { - m_client_ip = ip; - } - uint32_t getClientId() { - return m_client_idx; - } - void setClientId(uint32_t id) { - m_client_idx = id; - } - - uint32_t getServer(){ - return m_server_ip; - } - void setServer(uint32_t ip) { - m_server_ip = ip; - } - uint32_t getServerId(){ - return m_server_idx; - } - void setServerId(uint32_t id) { - m_server_idx = id; - } - uint16_t getServerPort() { - return m_server_port; - } - void setServerPort(uint16_t port) { - m_server_port = port; - } - uint16_t getClientPort() { - return m_client_port; - } - void setClientPort(uint16_t port) { - m_client_port = port; - } - mac_addr_align_t* getClientMac() { - return &m_client_mac; - } - void setClientMac(mac_addr_align_t* mac_info) { - if (mac_info != NULL) { - memcpy(&m_client_mac, mac_info, sizeof(mac_addr_align_t)); - m_client_mac.inused = INUSED; - } else { - m_client_mac.inused = UNUSED; - } - } -private: - uint32_t m_client_ip; - uint32_t m_client_idx; - uint32_t m_server_ip; - uint32_t m_server_idx; - mac_addr_align_t m_client_mac; - uint16_t m_client_port; - uint16_t m_server_port; + void generate_tuple(CTupleBase & tuple) { + tuple.setServer(m_ip); + } }; -class CFlowGenList; -mac_addr_align_t * get_mac_addr_by_ip(CFlowGenList *fl_list, - uint32_t ip); -bool is_mac_info_conf(CFlowGenList *fl_list); class CIpPool { public: @@ -381,14 +402,15 @@ class CIpPool { m_active_alloc++; return (port); } - bool is_valid_ip(uint32_t ip){ + + bool is_valid_ip(uint32_t ip){ CIpInfoBase* ip_front = m_ip_info.front(); CIpInfoBase* ip_back = m_ip_info.back(); if ((ip>=ip_front->get_ip()) && (ip<=ip_back->get_ip())) { return(true); } - printf("invalid ip:%x, min_ip:%x, max_ip:%x, this:%x\n", + printf("invalid ip:%x, min_ip:%x, max_ip:%x, this:%p\n", ip, ip_front->get_ip(), ip_back->get_ip(),this); return(false); @@ -422,6 +444,9 @@ class CIpPool { inc_cur_idx(); return res_idx; } + + + void set_dist(IP_DIST_t dist) { if (dist>=cdMAX_DIST) { m_dist = cdSEQ_DIST; @@ -491,13 +516,20 @@ class CIpPool { class CClientPool : public CIpPool { public: - void GenerateTuple(CTupleBase & tuple) { - uint32_t idx = generate_ip(); - tuple.setClientId(idx); - tuple.setClient(get_ip(idx)); - tuple.setClientMac(get_mac(idx)); - tuple.setClientPort(GenerateOnePort(idx)); + + uint32_t GenerateTuple(CTupleBase & tuple) { + uint32_t idx = generate_ip(); + CIpInfoBase* ip_info = m_ip_info[idx]; + ip_info->generate_tuple(tuple); + + tuple.setClientId(idx); + if (tuple.getClientPort()==ILLEGAL_PORT) { + m_port_allocation_error++; + } + m_active_alloc++; + return idx; } + uint16_t get_tcp_aging() { return m_tcp_aging; } @@ -509,10 +541,11 @@ public: uint32_t max_ip, double l_flow, double t_cps, - CFlowGenList* fl_list, + CFlowGenListMac* mac_info, bool has_mac_map, uint16_t tcp_aging, uint16_t udp_aging); + public: uint16_t m_tcp_aging; uint16_t m_udp_aging; @@ -568,11 +601,9 @@ private: class CServerPool : public CServerPoolBase { public: - CIpPool *gen; void GenerateTuple(CTupleBase & tuple) { uint32_t idx = gen->generate_ip(); - tuple.setServerId(idx); - tuple.setServer(gen->get_ip(idx)); + tuple.setServerAll(idx, gen->get_ip(idx)); } uint16_t GenerateOnePort(uint32_t idx) { return gen->GenerateOnePort(idx); @@ -587,11 +618,14 @@ public: if (gen!=NULL) { gen->Delete(); delete gen; + gen=NULL; } } uint32_t get_total_ips() { return gen->m_ip_info.size(); } +private: + CIpPool *gen; }; /* generate for each template */ @@ -648,7 +682,7 @@ public: public: CTupleGeneratorSmart(){ m_was_init=false; - has_mac_mapping = false; + m_has_mac_mapping = false; } bool Create(uint32_t _id, uint32_t thread_id, bool has_mac=false); @@ -672,7 +706,7 @@ public: uint32_t max_client, double l_flow, double t_cps, - CFlowGenList* fl_list, + CFlowGenListMac* mac_info, uint16_t tcp_aging, uint16_t udp_aging); bool add_server_pool(IP_DIST_t server_dist, @@ -699,7 +733,7 @@ private: std::vector<CClientPool*> m_client_pool; std::vector<CServerPoolBase*> m_server_pool; bool m_was_init; - bool has_mac_mapping; + bool m_has_mac_mapping; }; class CTupleTemplateGeneratorSmart { @@ -718,15 +752,13 @@ public: m_server_gen->GenerateTuple(tuple); m_cache_client_ip = tuple.getClient(); m_cache_client_idx = tuple.getClientId(); - m_cache_server_ip = tuple.getServer(); - m_cache_server_idx = tuple.getServerId(); + tuple.getServerAll(m_cache_server_idx, m_cache_server_ip); }else{ - tuple.setServer(m_cache_server_ip); - tuple.setServerId(m_cache_server_idx); - tuple.setClient(m_cache_client_ip); - tuple.setClientId(m_cache_client_idx); - tuple.setClientPort( - m_client_gen->GenerateOnePort(m_cache_client_idx)); + tuple.setServerAll(m_cache_server_idx, + m_cache_server_ip); + tuple.setClientAll2(m_cache_client_idx, + m_cache_client_ip, + m_client_gen->GenerateOnePort(m_cache_client_idx)); } m_cnt++; if (m_cnt>=m_w) { diff --git a/src/utl_yaml.cpp b/src/utl_yaml.cpp index 237e85af..5f3ca735 100755 --- a/src/utl_yaml.cpp +++ b/src/utl_yaml.cpp @@ -70,13 +70,15 @@ bool utl_yaml_read_ip_addr(const YAML::Node& node, std::string tmp; uint32_t ip; bool res=false; - try { + if ( node.FindValue(name) ) { node[name] >> tmp ; if ( my_inet_pton4((char *)tmp.c_str(), (unsigned char *)&ip) ){ val=PKT_NTOHL(ip); res=true; + }else{ + printf(" ERROR not a valid ip %s \n",(char *)tmp.c_str()); + exit(-1); } - }catch ( const std::exception& e ) { } return (res); } @@ -85,14 +87,11 @@ bool utl_yaml_read_uint32(const YAML::Node& node, std::string name, uint32_t & val){ bool res=false; - - try { + if ( node.FindValue(name) ) { node[name] >> val ; res=true; - }catch ( const std::exception& e ) { } return (res); - } bool utl_yaml_read_uint16(const YAML::Node& node, @@ -100,12 +99,10 @@ bool utl_yaml_read_uint16(const YAML::Node& node, uint16_t & val){ uint32_t val_tmp; bool res=false; - - try { + if ( node.FindValue(name) ) { node[name] >> val_tmp ; val = (uint16_t)val_tmp; res=true; - }catch ( const std::exception& e ) { } } @@ -113,10 +110,9 @@ bool utl_yaml_read_bool(const YAML::Node& node, std::string name, bool & val){ bool res=false; - try { + if ( node.FindValue(name) ) { node[name] >> val ; res=true; - }catch ( const std::exception& e ) { } return( res); } |