diff options
author | 2015-12-20 00:07:44 +0200 | |
---|---|---|
committer | 2015-12-20 00:07:44 +0200 | |
commit | 4ca24cf31919870a684fe78f17c856e0d220e6d5 (patch) | |
tree | f40ab95e52adca3ac713d61eb9fa3fd0d136e4ea /scripts/automation/trex_control_plane/client_utils | |
parent | 1895d21485621c3428d045fa0f5b9daf165c8260 (diff) | |
parent | 5cef472bcdc6c0b7e20e5cc42485ed5570c10f8c (diff) |
Merge branch 'master' into dan_stateless
Diffstat (limited to 'scripts/automation/trex_control_plane/client_utils')
3 files changed, 136 insertions, 71 deletions
diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index dd208da4..bdae7bd9 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -7,8 +7,7 @@ import general_utils import re from time import sleep from collections import namedtuple - -CmdResponse = namedtuple('CmdResponse', ['success', 'data']) +from common.trex_types import * class bcolors: BLUE = '\033[94m' @@ -33,21 +32,17 @@ class BatchMessage(object): def invoke(self, block = False): if not self.rpc_client.connected: - return False, "Not connected to server" + return RC_ERR("Not connected to server") msg = json.dumps(self.batch_list) - 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)]) - else: - return CmdResponse(rc, resp_list) + return self.rpc_client.send_raw_msg(msg) # JSON RPC v2.0 client class JsonRpcClient(object): - def __init__ (self, default_server, default_port): + def __init__ (self, default_server, default_port, prn_func = None): self.verbose = False self.connected = False @@ -56,6 +51,8 @@ class JsonRpcClient(object): self.server = default_server self.id_gen = general_utils.random_id_gen() + self.prn_func = prn_func + def get_connection_details (self): rc = {} rc['server'] = self.server @@ -112,7 +109,7 @@ class JsonRpcClient(object): def invoke_rpc_method (self, method_name, params = {}): if not self.connected: - return False, "Not connected to server" + return RC_ERR("Not connected to server") id, msg = self.create_jsonrpc_v2(method_name, params) @@ -130,11 +127,10 @@ class JsonRpcClient(object): self.socket.send(msg) break except zmq.Again: - sleep(0.1) tries += 1 if tries > 10: self.disconnect() - return CmdResponse(False, "Failed to send message to server") + return RC_ERR("*** [RPC] - Failed to send message to server") tries = 0 @@ -143,11 +139,10 @@ class JsonRpcClient(object): response = self.socket.recv() break except zmq.Again: - sleep(0.1) tries += 1 if tries > 10: self.disconnect() - return CmdResponse(False, "Failed to get server response") + return RC_ERR("*** [RPC] - Failed to get server response") self.verbose_msg("Server Response:\n\n" + self.pretty_json(response) + "\n") @@ -158,36 +153,35 @@ class JsonRpcClient(object): response_json = json.loads(response) if isinstance(response_json, list): - rc_list = [] + rc_batch = RC() for single_response in response_json: - rc, msg = self.process_single_response(single_response) - rc_list.append( CmdResponse(rc, msg) ) + rc = self.process_single_response(single_response) + rc_batch.add(rc) - return CmdResponse(True, rc_list) + return rc_batch else: - rc, msg = self.process_single_response(response_json) - return CmdResponse(rc, msg) + return self.process_single_response(response_json) def process_single_response (self, response_json): if (response_json.get("jsonrpc") != "2.0"): - return False, "Malformed Response ({0})".format(str(response_json)) + return RC_ERR("Malformed Response ({0})".format(str(response_json))) # error reported by server if ("error" in response_json): if "specific_err" in response_json["error"]: - return False, response_json["error"]["specific_err"] + return RC_ERR(response_json["error"]["specific_err"]) else: - return False, response_json["error"]["message"] + return RC_ERR(response_json["error"]["message"]) # if no error there should be a result if ("result" not in response_json): - return False, "Malformed Response ({0})".format(str(response_json)) + return RC_ERR("Malformed Response ({0})".format(str(response_json))) - return True, response_json["result"] + return RC_OK(response_json["result"]) @@ -199,11 +193,12 @@ class JsonRpcClient(object): self.socket.close(linger = 0) self.context.destroy(linger = 0) self.connected = False - return True, "" + return RC_OK() else: - return False, "Not connected to server" + return RC_ERR("Not connected to server") - def connect(self, server=None, port=None): + + def connect(self, server = None, port = None, prn_func = None): if self.connected: self.disconnect() @@ -215,20 +210,24 @@ class JsonRpcClient(object): # Socket to talk to server self.transport = "tcp://{0}:{1}".format(self.server, self.port) - print "\nConnecting To RPC Server On {0}".format(self.transport) + msg = "\nConnecting To RPC Server On {0}".format(self.transport) + if self.prn_func: + self.prn_func(msg) + else: + print msg self.socket = self.context.socket(zmq.REQ) try: self.socket.connect(self.transport) except zmq.error.ZMQError as e: - return False, "ZMQ Error: Bad server or port name: " + str(e) + return RC_ERR("ZMQ Error: Bad server or port name: " + str(e)) - self.socket.setsockopt(zmq.SNDTIMEO, 5) - self.socket.setsockopt(zmq.RCVTIMEO, 5) + self.socket.setsockopt(zmq.SNDTIMEO, 1000) + self.socket.setsockopt(zmq.RCVTIMEO, 1000) self.connected = True - return True, "" + return RC_OK() def reconnect(self): @@ -236,7 +235,7 @@ class JsonRpcClient(object): return self.connect() if not self.connected: - return False, "Not connected to server" + return RC_ERR("Not connected to server") # reconnect return self.connect(self.server, self.port) 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 3aeb6a34..d8070c74 100755 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -301,6 +301,30 @@ class CTRexPktBuilder(object): break return + def load_packet_from_pcap(self, pcap_path): + """ + This method loads a pcap file into a parsed packet builder object. + + :parameters: + pcap_path: str + a path to a pcap file, containing a SINGLE packet. + + :raises: + + :exc:`IOError`, in case provided path doesn't exists. + + """ + 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.load_packet(dpkt.ethernet.Ethernet(buf)) + else: + raise ValueError("Provided pcap file contains more than single packet.") + # arrive here ONLY if pcap contained SINGLE packet + return + def get_packet(self, get_ptr=False): """ This method provides access to the built packet, as an instance or as a pointer to packet itself. diff --git a/scripts/automation/trex_control_plane/client_utils/parsing_opts.py b/scripts/automation/trex_control_plane/client_utils/parsing_opts.py index 6c348467..6f9b4c6d 100755 --- a/scripts/automation/trex_control_plane/client_utils/parsing_opts.py +++ b/scripts/automation/trex_control_plane/client_utils/parsing_opts.py @@ -10,22 +10,24 @@ ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options']) # list of available parsing options MULTIPLIER = 1 -PORT_LIST = 2 -ALL_PORTS = 3 -PORT_LIST_WITH_ALL = 4 -FILE_PATH = 5 -FILE_FROM_DB = 6 -SERVER_IP = 7 -STREAM_FROM_PATH_OR_FILE = 8 -DURATION = 9 -FORCE = 10 - -TOTAL = 11 - -GLOBAL_STATS = 12 -PORT_STATS = 13 -PORT_STATUS = 14 -STATS_MASK = 15 +MULTIPLIER_STRICT = 2 +PORT_LIST = 3 +ALL_PORTS = 4 +PORT_LIST_WITH_ALL = 5 +FILE_PATH = 6 +FILE_FROM_DB = 7 +SERVER_IP = 8 +STREAM_FROM_PATH_OR_FILE = 9 +DURATION = 10 +FORCE = 11 +DRY_RUN = 12 +XTERM = 13 +TOTAL = 14 + +GLOBAL_STATS = 50 +PORT_STATS = 51 +PORT_STATUS = 52 +STATS_MASK = 53 # list of ArgumentGroup types MUTEX = 1 @@ -60,10 +62,15 @@ match_multiplier_help = """Multiplier should be passed in the following format: will provide a percentage of the line rate. examples : '-m 10', '-m 10kbps', '-m 10mpps', '-m 23%%' """ -def match_multiplier(val): - '''match some val against multiplier shortcut inputs ''' +def match_multiplier_common(val, strict_abs = True): - match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)$", val) + # on strict absolute we do not allow +/- + if strict_abs: + match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)$", val) + op = None + else: + match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)([\+\-])?$", val) + op = match.group(4) result = {} @@ -71,44 +78,53 @@ def match_multiplier(val): value = float(match.group(1)) unit = match.group(3) + + # raw type (factor) if not unit: result['type'] = 'raw' - result['max'] = value + result['value'] = value elif unit == 'bps': - result['type'] = 'max_bps' - result['max'] = value + result['type'] = 'bps' + result['value'] = value elif unit == 'kbps': - result['type'] = 'max_bps' - result['max'] = value * 1000 + result['type'] = 'bps' + result['value'] = value * 1000 elif unit == 'mbps': - result['type'] = 'max_bps' - result['max'] = value * 1000 * 1000 + result['type'] = 'bps' + result['value'] = value * 1000 * 1000 elif unit == 'gbps': - result['type'] = 'max_bps' - result['max'] = value * 1000 * 1000 * 1000 + result['type'] = 'bps' + result['value'] = value * 1000 * 1000 * 1000 elif unit == 'pps': - result['type'] = 'max_pps' - result['max'] = value + result['type'] = 'pps' + result['value'] = value elif unit == "kpps": - result['type'] = 'max_pps' - result['max'] = value * 1000 + result['type'] = 'pps' + result['value'] = value * 1000 elif unit == "mpps": - result['type'] = 'max_pps' - result['max'] = value * 1000 * 1000 + result['type'] = 'pps' + result['value'] = value * 1000 * 1000 elif unit == "%": - # will be translated by the port object result['type'] = 'percentage' - result['max'] = value + result['value'] = value + + + if op == "+": + result['op'] = "add" + elif op == "-": + result['op'] = "sub" + else: + result['op'] = "abs" return result @@ -116,6 +132,13 @@ def match_multiplier(val): raise argparse.ArgumentTypeError(match_multiplier_help) +def match_multiplier(val): + '''match some val against multiplier shortcut inputs ''' + return match_multiplier_common(val, strict_abs = False) + +def match_multiplier_strict(val): + '''match some val against multiplier shortcut inputs ''' + return match_multiplier_common(val, strict_abs = True) def is_valid_file(filename): if not os.path.isfile(filename): @@ -127,9 +150,14 @@ def is_valid_file(filename): OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'help': match_multiplier_help, 'dest': "mult", - 'default': {'type':'raw', 'max':1}, + 'default': {'type':'raw', 'value':1, 'op': 'abs'}, 'type': match_multiplier}), + MULTIPLIER_STRICT: ArgumentPack(['-m', '--multiplier'], + {'help': match_multiplier_help, + 'dest': "mult", + 'default': {'type':'raw', 'value':1, 'op': 'abs'}, + 'type': match_multiplier_strict}), TOTAL: ArgumentPack(['-t', '--total'], {'help': "traffic will be divided between all ports specified", @@ -177,6 +205,19 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'metavar': 'SERVER', 'help': "server IP"}), + DRY_RUN: ArgumentPack(['-n', '--dry'], + {'action': 'store_true', + 'dest': 'dry', + 'default': False, + 'help': "Dry run - no traffic will be injected"}), + + + XTERM: ArgumentPack(['-x', '--xterm'], + {'action': 'store_true', + 'dest': 'xterm', + 'default': False, + 'help': "Starts TUI in xterm window"}), + GLOBAL_STATS: ArgumentPack(['-g'], {'action': 'store_true', 'help': "Fetch only global statistics"}), @@ -189,6 +230,7 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'action': 'store_true', 'help': "Fetch only port status data"}), + # advanced options PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST, ALL_PORTS], |