From 29de5a33617b44c79982670a984ba46ec0e3a8d8 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 20 Sep 2015 16:47:40 +0300 Subject: temporary interactive stateless shell --- .../examples/interactive_stateless.py | 128 +++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 scripts/automation/trex_control_plane/examples/interactive_stateless.py diff --git a/scripts/automation/trex_control_plane/examples/interactive_stateless.py b/scripts/automation/trex_control_plane/examples/interactive_stateless.py new file mode 100644 index 00000000..7c25b4ef --- /dev/null +++ b/scripts/automation/trex_control_plane/examples/interactive_stateless.py @@ -0,0 +1,128 @@ +#!/router/bin/python + +import trex_root_path +from client.trex_stateless_client import * +from common.trex_exceptions import * +import cmd +from termstyle import termstyle +# import termstyle +import os +from argparse import ArgumentParser +import socket +import errno +import ast +import json + + +class InteractiveStatelessTRex(cmd.Cmd): + + intro = termstyle.green("\nInteractive shell to play with Cisco's TRex stateless API.\ + \nType help to view available pre-defined scenarios\n(c) All rights reserved.\n") + prompt = '> ' + + def __init__(self, trex_host, trex_port, virtual, verbose): + cmd.Cmd.__init__(self) + + self.verbose = verbose + self.virtual = virtual + self.trex = CTRexStatelessClient(trex_host, trex_port, self.virtual) + self.DEFAULT_RUN_PARAMS = dict(m=1.5, + nc=True, + p=True, + d=100, + f='avl/sfr_delay_10_1g.yaml', + l=1000) + self.run_params = dict(self.DEFAULT_RUN_PARAMS) + + def do_transmit(self, line): + """Transmits a request over using a given link to server.\ + \nuse: transmit [method_name] [method_params]""" + if line == "": + print "\nUsage: [method name] [param dict as string]\n" + print "Example: rpc test_add {'x': 12, 'y': 17}\n" + return + + args = line.split(' ', 1) # args will have max length of 2 + method_name = args[0] + params = None + bad_parse = False + + try: + params = ast.literal_eval(args[1]) + if not isinstance(params, dict): + bad_parse = True + except ValueError as e1: + bad_parse = True + except SyntaxError as e2: + bad_parse = True + + if bad_parse: + print "\nValue should be a valid dict: '{0}'".format(args[1]) + print "\nUsage: [method name] [param dict as string]\n" + print "Example: rpc test_add {'x': 12, 'y': 17}\n" + return + + response = self.trex.transmit(method_name, params) + if not self.virtual: + # expect response + rc, msg = response + if rc: + print "\nServer Response:\n\n" + json.dumps(msg) + "\n" + else: + print "\n*** " + msg + "\n" + + + + + + def do_push_files(self, filepaths): + """Pushes a custom file to be stored locally on T-Rex 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( + 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) ***") + else: + print termstyle.magenta("*** End of T-Rex push_files method (failed) ***") + + except IOError as inst: + print termstyle.magenta(inst) + +if __name__ == "__main__": + parser = ArgumentParser(description=termstyle.cyan('Run TRex client stateless API demos and scenarios.'), + usage="client_interactive_example [options]") + + 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.", + 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") + # 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", + action="store_true", + help="Switch ON virtual option at TRex client. Default is: OFF.", + default=False) + parser.add_argument("--verbose", dest="verbose", + action="store_true", + help="Switch ON verbose option at TRex client. Default is: OFF.", + default=False) + args = parser.parse_args() + + try: + InteractiveStatelessTRex(**vars(args)).cmdloop() + + except KeyboardInterrupt: + print termstyle.cyan('Bye Bye!') + exit(-1) + except socket.error, e: + if e.errno == errno.ECONNREFUSED: + raise socket.error(errno.ECONNREFUSED, + "Connection from T-Rex server was terminated. \ + Please make sure the server is up.") -- cgit 1.2.3-korg From 20a7b8ed2a75debc3f2015d571fb4faf2cfc8b13 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 20 Sep 2015 23:56:04 +0300 Subject: minor updated to client API library, adding the CRxStats class --- .../client/trex_stateless_client.py | 46 ++++++++++++++++++++-- .../client_utils/packet_builder.py | 1 - 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 5513f420..a2c148ce 100644 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -7,7 +7,8 @@ except ImportError: # support import for Python 3 import client.outer_packages from client_utils.jsonrpc_client import JsonRpcClient - +from client_utils.packet_builder import CTRexPktBuilder +import json class CTRexStatelessClient(object): @@ -16,11 +17,50 @@ class CTRexStatelessClient(object): super(CTRexStatelessClient, self).__init__() self.tx_link = CTRexStatelessClient.CTxLink(server, port, virtual) + def add_stream(self): + pass - def transmit(self, method_name, params = {}): + def transmit(self, method_name, params={}): return self.tx_link.transmit(method_name, params) + # ------ private classes ------ # + class CRxStats(object): + + def __init__(self, enabled=False, seq_enabled=False, latency_enabled=False): + self._rx_dict = {"enabled" : enabled, + "seq_enabled" : seq_enabled, + "latency_enabled" : latency_enabled} + + @property + def enabled(self): + return self._rx_dict.get("enabled") + + @enabled.setter + def enabled(self, bool_value): + self._rx_dict['enabled'] = bool_value + + @property + def seq_enabled(self): + return self._rx_dict.get("seq_enabled") + + @seq_enabled.setter + def seq_enabled(self, bool_value): + self._rx_dict['seq_enabled'] = bool_value + + @property + def latency_enabled(self): + return self._rx_dict.get("latency_enabled") + + @latency_enabled.setter + def latency_enabled(self, bool_value): + self._rx_dict['latency_enabled'] = bool_value + + def dump(self): + return json.dumps({i:self._rx_dict.get(i) + for i in self._rx_dict.keys() + if self._rx_dict.get(i) + }) class CTxLink(object): """describes the connectivity of the stateless client method""" @@ -33,7 +73,7 @@ class CTRexStatelessClient(object): if not self.virtual: self.rpc_link.connect() - def transmit(self, method_name, params = {}): + def transmit(self, method_name, params={}): if self.virtual: print "Transmitting virtually over tcp://{server}:{port}".format( server=self.server, 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 fc34d931..5ca8da93 100644 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -302,7 +302,6 @@ class CTRexPktBuilder(object): except IOError: raise IOError(2, "The provided path could not be accessed") - # ----- useful shortcut methods ----- # def gen_dns_packet(self): pass -- cgit 1.2.3-korg From 2965b8f7e7ca924af9ce6d30b2c0b586ec3e21c5 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Mon, 21 Sep 2015 11:22:53 +0300 Subject: Updated implementation of add_stream method, created subclasses to reflect vairous objects. --- .../client/trex_stateless_client.py | 101 +++++++++++++++++++-- 1 file changed, 92 insertions(+), 9 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index a2c148ce..322d7319 100644 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -17,14 +17,66 @@ class CTRexStatelessClient(object): super(CTRexStatelessClient, self).__init__() self.tx_link = CTRexStatelessClient.CTxLink(server, port, virtual) - def add_stream(self): - pass + def add_stream(self, handler, port_id, stream_id, stream_obj): + assert isinstance(stream_obj, CTRexStatelessClient.CStream) + params = {"handler":handler, + "port_id":port_id, + "stream_id":stream_id, + "stream":stream_obj.dump()} + return self.transmit("add_stream", params) def transmit(self, method_name, params={}): return self.tx_link.transmit(method_name, params) # ------ private classes ------ # + class CStream(object): + """docstring for CStream""" + def __init__(self): + super(CStream, self).__init__() + self.packet = CTRexPktBuilder() + self.rx_stats = CRxStats() + self.mode = CTxMode() + self.isg + self._next_stream = -1 + self._self_start + self._enabled + + @property + def enabled(self): + return self._enabled + + @enabled.setter + def enabled(self, bool_value): + self._enabled = bool(bool_value) + + @property + def self_start(self): + return self._self_start + + @enabled.setter + def self_start(self, bool_value): + self._self_start = bool(bool_value) + + @property + def next_stream(self): + return self._next_stream + + @enabled.setter + def next_stream(self, value): + self._next_stream = int(bool_value) + + def dump(self): + pass + return {"enabled":self.enabled, + "self_start":self.self_start, + "isg":self.isg, + "next_stream":self.next_stream, + "packet":self.packet.dump_pkt(), + "mode":self.mode.dump(), + "vm":self.packet.dump_vm_instructions(), # TODO - add this method to packet builder module + "rx_stats":self.rx_stats.dump()}) + class CRxStats(object): def __init__(self, enabled=False, seq_enabled=False, latency_enabled=False): @@ -38,7 +90,7 @@ class CTRexStatelessClient(object): @enabled.setter def enabled(self, bool_value): - self._rx_dict['enabled'] = bool_value + self._rx_dict['enabled'] = bool(bool_value) @property def seq_enabled(self): @@ -46,7 +98,7 @@ class CTRexStatelessClient(object): @seq_enabled.setter def seq_enabled(self, bool_value): - self._rx_dict['seq_enabled'] = bool_value + self._rx_dict['seq_enabled'] = bool(bool_value) @property def latency_enabled(self): @@ -54,13 +106,44 @@ class CTRexStatelessClient(object): @latency_enabled.setter def latency_enabled(self, bool_value): - self._rx_dict['latency_enabled'] = bool_value + self._rx_dict['latency_enabled'] = bool(bool_value) + + def dump(self): + return {k:v + for k,v in self._rx_dict.items() + if v + }) + + class CTxMode(object): + """docstring for CTxMode""" + def __init__(self, tx_mode, pps): + super(CTxMode, self).__init__() + if tx_mode not in ["continuous", "single_burst", "multi_burst"]: + raise ValueError("Unknown TX mode ('{0}')has been initialized.".format(tx_mode)) + self._tx_mode = tx_mode + self._fields = {'pps':float(pps)} + if tx_mode == "single_burst": + self._fields['total_pkts'] = 0 + elif tx_mode == "multi_burst": + self._fields['pkts_per_burst'] = 0 + self._fields['ibg'] = 0.0 + self._fields['count'] = 0 + else: + pass + + def set_tx_mode_attr(self, attr, val): + if attr in self._fields: + self._fields[attr] = type(self._fields.get(attr))(val) + else: + raise ValueError("The provided attribute ('{0}') is not a legal attribute in selected TX mode ('{1}')". + format(attr, self._tx_mode)) def dump(self): - return json.dumps({i:self._rx_dict.get(i) - for i in self._rx_dict.keys() - if self._rx_dict.get(i) - }) + dump = {"type":self._tx_mode} + dump.update({k:v + for k, v in self._fields.items() + }) + return dump class CTxLink(object): """describes the connectivity of the stateless client method""" -- cgit 1.2.3-korg From b3ac7facbbbc4815388298534fdfdd161ce89534 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 22 Sep 2015 13:56:49 +0300 Subject: updated packet builder and stateless client modules --- .../client/trex_stateless_client.py | 14 +- .../client_utils/packet_builder.py | 145 ++++++++++++++++----- 2 files changed, 117 insertions(+), 42 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 322d7319..12f6eff6 100644 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -33,10 +33,10 @@ class CTRexStatelessClient(object): class CStream(object): """docstring for CStream""" def __init__(self): - super(CStream, self).__init__() + super(CTRexStatelessClient.CStream, self).__init__() self.packet = CTRexPktBuilder() - self.rx_stats = CRxStats() - self.mode = CTxMode() + self.rx_stats = CTRexStatelessClient.CRxStats() + self.mode = CTRexStatelessClient.CTxMode() self.isg self._next_stream = -1 self._self_start @@ -54,7 +54,7 @@ class CTRexStatelessClient(object): def self_start(self): return self._self_start - @enabled.setter + @self_start.setter def self_start(self, bool_value): self._self_start = bool(bool_value) @@ -62,7 +62,7 @@ class CTRexStatelessClient(object): def next_stream(self): return self._next_stream - @enabled.setter + @next_stream.setter def next_stream(self, value): self._next_stream = int(bool_value) @@ -75,7 +75,7 @@ class CTRexStatelessClient(object): "packet":self.packet.dump_pkt(), "mode":self.mode.dump(), "vm":self.packet.dump_vm_instructions(), # TODO - add this method to packet builder module - "rx_stats":self.rx_stats.dump()}) + "rx_stats":self.rx_stats.dump()} class CRxStats(object): @@ -112,7 +112,7 @@ class CTRexStatelessClient(object): return {k:v for k,v in self._rx_dict.items() if v - }) + } class CTxMode(object): """docstring for CTxMode""" 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 5ca8da93..60f1d2f4 100644 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -1,6 +1,5 @@ #!/router/bin/python - import outer_packages import dpkt import socket @@ -10,6 +9,7 @@ import random import string import struct import re +from abc import ABCMeta, abstractmethod class CTRexPktBuilder(object): @@ -30,7 +30,7 @@ class CTRexPktBuilder(object): self._pkt_by_hdr = {} self._pkt_top_layer = None self._max_pkt_size = max_pkt_size - self.payload_generator = CTRexPktBuilder.CTRexPayloadGen(self._packet, self._max_pkt_size) + self.payload_gen = CTRexPktBuilder.CTRexPayloadGen(self._packet, self._max_pkt_size) self.vm = CTRexPktBuilder.CTRexVM() def add_pkt_layer(self, layer_name, pkt_layer): @@ -341,6 +341,9 @@ class CTRexPktBuilder(object): if self._pkt_by_hdr[layer] is layer_obj: return layer + def _calc_offset(self, layer_name, hdr_field): + pass + @staticmethod def _decode_mac_addr(mac_addr): """ @@ -457,6 +460,8 @@ class CTRexPktBuilder(object): """ super(CTRexPktBuilder.CTRexVM, self).__init__() self.vm_variables = {} + self.vm_inst_by_offset = {} + # self.vm_inst_offsets = [] def set_vm_var_field(self, var_name, field_name, val): """ @@ -478,7 +483,7 @@ class CTRexPktBuilder(object): """ return self.vm_variables[var_name].set_field(field_name, val) - def add_flow_man_simple(self, name, **kwargs): + def add_flow_man_inst(self, name, **kwargs): """ Adds a new flow manipulation object to the VM instance. @@ -500,13 +505,19 @@ class CTRexPktBuilder(object): Will rise when VM variables were misconfiguration. """ if name not in self.vm_variables.keys(): - self.vm_variables[name] = self.CTRexVMVariable(name) + self.vm_variables[name] = self.CTRexVMFlowVariable(name) # try configuring VM var attributes for (field, value) in kwargs.items(): self.vm_variables[name].set_field(field, value) else: raise CTRexPktBuilder.VMVarNameExistsError(name) + def add_checksum_inst(self, linked_ip_layer): + pass + + def add_write_flow_inst(self): + pass + def load_flow_man(self, flow_obj): """ Loads an outer VM variable (instruction) into current VM. @@ -520,7 +531,7 @@ class CTRexPktBuilder(object): list holds variables data of VM """ - assert isinstance(flow_obj, CTRexPktBuilder.CTRexVM.CTRexVMVariable) + assert isinstance(flow_obj, CTRexPktBuilder.CTRexVM.CTRexVMFlowVariable) if flow_obj.name not in self.vm_variables.keys(): self.vm_variables[flow_obj.name] = flow_obj else: @@ -540,7 +551,30 @@ class CTRexPktBuilder(object): return [var.dump() for key, var in self.vm_variables.items()] - class CTRexVMVariable(object): + class CVMAbstractInstruction(object): + __metaclass__ = ABCMeta + + def __init__(self, name): + """ + Instantiate a CTRexVMVariable object + + :parameters: + name : str + a string representing the name of the VM variable. + """ + super(CTRexPktBuilder.CTRexVM.CVMAbstractInstruction, self).__init__() + self.name = name + + def set_field(self, field_name, val): + if not hasattr(self, field_name): + raise CTRexPktBuilder.VMFieldNameError(field_name) + setattr(self, field_name, val) + + @abstractmethod + def dump(self): + pass + + class CTRexVMFlowVariable(CVMAbstractInstruction): """ This class defines a single VM variable to be used as part of CTRexVar object. """ @@ -555,12 +589,12 @@ class CTRexPktBuilder(object): name : str a string representing the name of the VM variable. """ - super(CTRexPktBuilder.CTRexVM.CTRexVMVariable, self).__init__() - self.name = name + super(CTRexPktBuilder.CTRexVM.CTRexVMFlowVariable, self).__init__(name) + # self.name = name self.size = 4 self.big_endian = True self.operation = "inc" - self.split_by_core = False + # self.split_by_core = False self.init_value = 1 self.min_value = self.init_value self.max_value = self.init_value @@ -585,32 +619,25 @@ class CTRexPktBuilder(object): """ if not hasattr(self, field_name): - raise CTRexPktBuilder.VMVarNameError(field_name) + raise CTRexPktBuilder.VMFieldNameError(field_name) elif field_name == "size": if type(val) != int: - raise CTRexPktBuilder.VMVarFieldTypeError("size", int) + raise CTRexPktBuilder.VMFieldTypeError("size", int) elif val not in self.VALID_SIZE: - raise CTRexPktBuilder.VMVarValueError("size", self.VALID_SIZE) + raise CTRexPktBuilder.VMFieldValueError("size", self.VALID_SIZE) elif field_name == "init_value": if type(val) != int: - raise CTRexPktBuilder.VMVarFieldTypeError("init_value", int) + raise CTRexPktBuilder.VMFieldTypeError("init_value", int) elif field_name == "operation": if type(val) != str: - raise CTRexPktBuilder.VMVarFieldTypeError("operation", str) + raise CTRexPktBuilder.VMFieldTypeError("operation", str) elif val not in self.VALID_OPERATION: - raise CTRexPktBuilder.VMVarValueError("operation", self.VALID_OPERATION) - elif field_name == "split_by_core": - val = bool(val) + raise CTRexPktBuilder.VMFieldValueError("operation", self.VALID_OPERATION) + # elif field_name == "split_by_core": + # val = bool(val) # update field value on success setattr(self, field_name, val) - def is_valid(self): - if self.size not in self.VALID_SIZE: - return False - if self.type not in self.VALID_OPERATION: - return False - return True - def dump(self): """ dumps a variable fields in a dictionary data structure. @@ -623,15 +650,63 @@ class CTRexPktBuilder(object): """ return {"ins_name": "flow_man_simple", # VM variable dump always refers to manipulate instruction. - "flow_variable_name": self.name, - "object_size": self.size, - # "big_endian": self.big_endian, - "Operation": self.operation, - "split_by_core": self.split_by_core, + "name": self.name, + "size": self.size, + "op": self.operation, + # "split_by_core": self.split_by_core, "init_value": self.init_value, "min_value": self.min_value, "max_value": self.max_value} + class CTRexVMChecksumInst(CVMAbstractInstruction): + + def __init__(self, name, offset=0): + """ + Instantiate a CTRexVMChecksumInst object + + :parameters: + name : str + a string representing the name of the VM variable. + """ + super(CTRexPktBuilder.CTRexVM.CTRexVMChecksumInst, self).__init__(name) + self.pkt_offset = offset + + def dump(self): + return {"type": "fix_checksum_ipv4", + "pkt_offset": int(self.pkt_offset)} + + class CTRexVMWrtFlowVarInst(CVMAbstractInstruction): + + def __init__(self, name): + """ + Instantiate a CTRexVMWrtFlowVarInst object + + :parameters: + name : str + a string representing the name of the VM variable. + """ + super(CTRexPktBuilder.CTRexVM.CTRexVMWrtFlowVarInst, self).__init__(name) + self.pkt_offset = 0 + self.add_value = 0 + self.is_big_endian = False + + def set_field(self, field_name, val): + if not hasattr(self, field_name): + raise CTRexPktBuilder.VMFieldNameError(field_name) + cur_attr_type = getattr(self, field_name) + if cur_attr_type == type(val): + setattr(self, field_name, val) + else: + CTRexPktBuilder.VMFieldTypeError(field_name, cur_attr_type) + + def dump(self): + return {"type": "write_flow_var", + "name": self.name, + "pkt_offset": int(self.pkt_offset), + "add_value": int(self.add_value), + "is_big_endian": bool(self.is_big_endian) + } + class CPacketBuildException(Exception): """ This is the general Packet Building error exception class. @@ -682,16 +757,16 @@ class CTRexPktBuilder(object): self.message = message or self._default_message super(CTRexPktBuilder.VMVarNameExistsError, self).__init__(-21, self.message) - class VMVarNameError(CPacketBuildException): + class VMFieldNameError(CPacketBuildException): """ This exception is used to indicate that an undefined VM var field name has been accessed. """ def __init__(self, name, message=''): self._default_message = "The given VM field name ({0}) is not defined and isn't legal.".format(name) self.message = message or self._default_message - super(CTRexPktBuilder.VMVarNameError, self).__init__(-22, self.message) + super(CTRexPktBuilder.VMFieldNameError, self).__init__(-22, self.message) - class VMVarFieldTypeError(CPacketBuildException): + class VMFieldTypeError(CPacketBuildException): """ This exception is used to indicate an illegal value has type has been given to VM variable field. """ @@ -701,9 +776,9 @@ class CTRexPktBuilder(object): field_type=type(name).__name__, allowed_type=ok_type.__name__) self.message = message or self._default_message - super(CTRexPktBuilder.VMVarFieldTypeError, self).__init__(-31, self.message) + super(CTRexPktBuilder.VMFieldTypeError, self).__init__(-31, self.message) - class VMVarValueError(CPacketBuildException): + class VMFieldValueError(CPacketBuildException): """ This exception is used to indicate an error an illegal value has been assigned to VM variable field. """ @@ -712,7 +787,7 @@ class CTRexPktBuilder(object): The only allowed options are: {allowed_opts}.'.format(field_name=name, allowed_opts=ok_opts) self.message = message or self._default_message - super(CTRexPktBuilder.VMVarValueError, self).__init__(-32, self.message) + super(CTRexPktBuilder.VMFieldValueError, self).__init__(-32, self.message) if __name__ == "__main__": -- cgit 1.2.3-korg From fb408d1f3a92edeb74b86f94aa2dddf5a0743513 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Wed, 23 Sep 2015 23:59:52 +0300 Subject: Finished writing infra layer of all VM instructions --- .../client_utils/packet_builder.py | 61 ++++++++++++++++------ 1 file changed, 45 insertions(+), 16 deletions(-) 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 60f1d2f4..1dd8b309 100644 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -10,6 +10,7 @@ import string import struct import re from abc import ABCMeta, abstractmethod +from collections import namedtuple class CTRexPktBuilder(object): @@ -238,13 +239,13 @@ class CTRexPktBuilder(object): return copy.copy(layer) if layer else None # VM access methods - def set_vm_ip_range(self, ip_start, ip_end, ip_type="ipv4"): + def set_vm_ip_range(self, ip_layer_name, ip_start, ip_end, ip_type="ipv4", add_checksum_inst=True): pass - def set_vm_range_type(self, ip_type): + def set_vm_eth_range(self, eth_layer_name, mac_start, mac_end): pass - def set_vm_core_mask(self, ip_type): + def set_vm_range_type(self, start_val, end_val): pass def get_vm_data(self): @@ -451,6 +452,8 @@ class CTRexPktBuilder(object): This class defines the TRex VM which represents how TRex will regenerate packets. The packets will be regenerated based on the built packet containing this class. """ + InstStore = namedtuple('InstStore', ['type', 'inst']) + def __init__(self): """ Instantiate a CTRexVM object @@ -460,7 +463,7 @@ class CTRexPktBuilder(object): """ super(CTRexPktBuilder.CTRexVM, self).__init__() self.vm_variables = {} - self.vm_inst_by_offset = {} + self._inst_by_offset = {} # this data structure holds only offset-related instructions, ordered in tuples # self.vm_inst_offsets = [] def set_vm_var_field(self, var_name, field_name, val): @@ -504,19 +507,38 @@ class CTRexPktBuilder(object): + Exceptions from :func:`CTRexPktBuilder.CTRexVM.CTRexVMVariable.set_field` method. Will rise when VM variables were misconfiguration. """ - if name not in self.vm_variables.keys(): + if name not in self.vm_variables: self.vm_variables[name] = self.CTRexVMFlowVariable(name) - # try configuring VM var attributes + # try configuring VM instruction attributes for (field, value) in kwargs.items(): self.vm_variables[name].set_field(field, value) else: raise CTRexPktBuilder.VMVarNameExistsError(name) - def add_checksum_inst(self, linked_ip_layer): + def add_fix_checksum_inst(self, linked_ipv4_obj, offset_to_obj=20, name=None): + # check if specified linked_ipv4_obj is indeed an ipv4 object + if not (isinstance(linked_ipv4_obj, dpkt.ip.IP) ): + raise ValueError("The provided layer object is not of IPv4.") + checksum_offset = offset_to_obj + 10 # IPv4 header places checksum field at the 10-12 Bytes + if not name: + name = "checksum_{off}".format(off = checksum_offset) # name will override previous checksum inst, OK + new_checksum_inst = self.CTRexVMChecksumInst(name, checksum_offset) + # store the checksum inst in the end of the IP header (20 Bytes long) + self._inst_by_offset[offset_to_obj + 20] = self.InstStore('checksum', new_checksum_inst) + + def add_write_flow_inst(self, name, pkt_offset, **kwargs): pass + if name not in self.vm_variables: + raise KeyError("Trying to add write_flow_var instruction to a not-exists VM flow variable ('{0}')". + format(name)) + else: + new_write_inst = self.CTRexVMWrtFlowVarInst(name, pkt_offset) + # try configuring VM instruction attributes + for (field, value) in kwargs.items(): + new_write_inst.set_field(field, value) + # add the instruction to the date-structure + self._inst_by_offset[pkt_offset] = self.InstStore('write', new_write_inst) - def add_write_flow_inst(self): - pass def load_flow_man(self, flow_obj): """ @@ -539,7 +561,7 @@ class CTRexPktBuilder(object): def dump(self): """ - dumps a VM variables (instructions) into an list data structure. + dumps a VM variables (instructions) into a list data structure. :parameters: None @@ -548,8 +570,13 @@ class CTRexPktBuilder(object): list holds variables data of VM """ - return [var.dump() - for key, var in self.vm_variables.items()] + # at first, dump all CTRexVMFlowVariable instructions + ret_val = [var.dump() + for key, var in self.vm_variables.items()] + # then, dump all the CTRexVMWrtFlowVarInst and CTRexVMChecksumInst instructions + ret_val += [self._inst_by_offset.get(key).inst.dump() + for key in sorted(self._inst_by_offset)] + return ret_val class CVMAbstractInstruction(object): __metaclass__ = ABCMeta @@ -660,7 +687,7 @@ class CTRexPktBuilder(object): class CTRexVMChecksumInst(CVMAbstractInstruction): - def __init__(self, name, offset=0): + def __init__(self, name, offset): """ Instantiate a CTRexVMChecksumInst object @@ -677,7 +704,7 @@ class CTRexPktBuilder(object): class CTRexVMWrtFlowVarInst(CVMAbstractInstruction): - def __init__(self, name): + def __init__(self, name, pkt_offset): """ Instantiate a CTRexVMWrtFlowVarInst object @@ -686,13 +713,15 @@ class CTRexPktBuilder(object): a string representing the name of the VM variable. """ super(CTRexPktBuilder.CTRexVM.CTRexVMWrtFlowVarInst, self).__init__(name) - self.pkt_offset = 0 + self.pkt_offset = int(pkt_offset) self.add_value = 0 self.is_big_endian = False def set_field(self, field_name, val): if not hasattr(self, field_name): raise CTRexPktBuilder.VMFieldNameError(field_name) + elif field_name == 'pkt_offset': + raise ValueError("pkt_offset value cannot be changed") cur_attr_type = getattr(self, field_name) if cur_attr_type == type(val): setattr(self, field_name, val) @@ -702,7 +731,7 @@ class CTRexPktBuilder(object): def dump(self): return {"type": "write_flow_var", "name": self.name, - "pkt_offset": int(self.pkt_offset), + "pkt_offset": self.pkt_offset, "add_value": int(self.add_value), "is_big_endian": bool(self.is_big_endian) } -- cgit 1.2.3-korg From 281e2eaaea59bcfa7a0b67dcc3fcbd92a1e11d8c Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 24 Sep 2015 01:19:02 +0300 Subject: Progress in ip_range methods --- .../client_utils/packet_builder.py | 77 ++++++++++++++++++++-- 1 file changed, 72 insertions(+), 5 deletions(-) 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 1dd8b309..2fe88b48 100644 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -239,8 +239,35 @@ class CTRexPktBuilder(object): return copy.copy(layer) if layer else None # VM access methods - def set_vm_ip_range(self, ip_layer_name, ip_start, ip_end, ip_type="ipv4", add_checksum_inst=True): - pass + def set_vm_ip_range(self, ip_layer_name, ip_field, + ip_init, ip_start, ip_end, + operation, ip_type="ipv4", add_checksum_inst=True): + if ip_field not in ["src", "dst"]: + raise ValueError("set_vm_ip_range only available for source ('src') or destination ('dst') ip addresses") + # set differences between IPv4 and IPv6 + if ip_type == "ipv4": + ip_class = dpkt.ip.IP + ip_addr_size = 4 + elif ip_type == "ipv6": + ip_class = dpkt.ip6.IP6 + ip_addr_size = 16 + else: + raise CTRexPktBuilder.IPAddressError() + + self._verify_layer_prop(ip_layer_name, ip_class) + init_val = CTRexPktBuilder._decode_ip_addr(ip_init, ip_type) + start_val = CTRexPktBuilder._decode_ip_addr(ip_start, ip_type) + end_val = CTRexPktBuilder._decode_ip_addr(ip_end, ip_type) + # All validations are done, start adding VM instructions + flow_var_name = "{layer}__{field}".format(layer = ip_layer_name, field = ip_field) + field_abs_offset = self._calc_offset(ip_layer_name, ip_field) + + self.vm.add_flow_man_inst(flow_var_name, size = ip_addr_size, operation = operation, + init_value = init_val, + min_value = start_val, + max_value = end_val) + + def set_vm_eth_range(self, eth_layer_name, mac_start, mac_end): pass @@ -343,7 +370,26 @@ class CTRexPktBuilder(object): return layer def _calc_offset(self, layer_name, hdr_field): - pass + pkt_header = self._pkt_by_hdr.get(layer_name) + hdr_offset = len(self._packet) - len(pkt_header) + inner_hdr_offsets = [] + for field in pkt_header.__hdr__: + if field == hdr_field: + break + else: + inner_hdr_offsets.append(struct.calcsize(field[1])) + return pkt_header + sum(inner_hdr_offsets) + + def _verify_layer_prop(self, layer_name, layer_type = None): + if layer_name not in self._pkt_by_hdr: + raise CTRexPktBuilder.PacketLayerError(layer_name) + pkt_layer = self._pkt_by_hdr.get(layer_name) + if layer_type: + # check for layer type + if not isinstance(pkt_layer, layer_type): + raise CTRexPktBuilder.PacketLayerTypeError(layer_name, type(pkt_layer), layer_type) + return + @staticmethod def _decode_mac_addr(mac_addr): @@ -605,7 +651,7 @@ class CTRexPktBuilder(object): """ This class defines a single VM variable to be used as part of CTRexVar object. """ - VALID_SIZE = [1, 2, 4, 8] + VALID_SIZE = [1, 2, 4, 8] # size in Bytes VALID_OPERATION = ["inc", "dec", "random"] def __init__(self, name): @@ -775,7 +821,28 @@ class CTRexPktBuilder(object): def __init__(self, message=''): self._default_message = 'Illegal MAC address has been provided.' self.message = message or self._default_message - super(CTRexPktBuilder.MACAddressError, self).__init__(-11, self.message) + super(CTRexPktBuilder.MACAddressError, self).__init__(-12, self.message) + + class PacketLayerError(CPacketBuildException): + """ + This exception is used to indicate an error caused by operation performed on an non-exists layer of the packet. + """ + def __init__(self, name, message=''): + self._default_message = "The given packet layer name ({0}) does not exists.".format(name) + self.message = message or self._default_message + super(CTRexPktBuilder.PacketLayerError, self).__init__(-13, self.message) + + class PacketLayerTypeError(CPacketBuildException): + """ + This exception is used to indicate an error caused by operation performed on an non-exists layer of the packet. + """ + def __init__(self, name, layer_type, ok_type, message=''): + self._default_message = 'The type of packet layer {layer_name} is of type {layer_type}, \ + and not of the expected {allowed_type}.'.format(layer_name=name, + layer_type=layer_type, + allowed_type=ok_type.__name__) + self.message = message or self._default_message + super(CTRexPktBuilder.PacketLayerTypeError, self).__init__(-13, self.message) class VMVarNameExistsError(CPacketBuildException): """ -- cgit 1.2.3-korg From 186ca35e6ceabfeea7c0899a5ad50158f669243f Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 24 Sep 2015 02:17:00 +0300 Subject: Finished implementing the ranging VM of IP Eth and custom fields --- .../client_utils/packet_builder.py | 70 +++++++++++++++++----- 1 file changed, 55 insertions(+), 15 deletions(-) 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 2fe88b48..8fd74b1c 100644 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -240,8 +240,9 @@ class CTRexPktBuilder(object): # VM access methods def set_vm_ip_range(self, ip_layer_name, ip_field, - ip_init, ip_start, ip_end, - operation, ip_type="ipv4", add_checksum_inst=True): + ip_init, ip_start, ip_end, add_value, + operation, is_big_endian=False, + ip_type="ipv4", add_checksum_inst=True): if ip_field not in ["src", "dst"]: raise ValueError("set_vm_ip_range only available for source ('src') or destination ('dst') ip addresses") # set differences between IPv4 and IPv6 @@ -260,20 +261,53 @@ class CTRexPktBuilder(object): end_val = CTRexPktBuilder._decode_ip_addr(ip_end, ip_type) # All validations are done, start adding VM instructions flow_var_name = "{layer}__{field}".format(layer = ip_layer_name, field = ip_field) - field_abs_offset = self._calc_offset(ip_layer_name, ip_field) - + hdr_offset, field_abs_offset = self._calc_offset(ip_layer_name, ip_field) self.vm.add_flow_man_inst(flow_var_name, size = ip_addr_size, operation = operation, init_value = init_val, min_value = start_val, max_value = end_val) - - - - def set_vm_eth_range(self, eth_layer_name, mac_start, mac_end): - pass - - def set_vm_range_type(self, start_val, end_val): - pass + self.vm.add_write_flow_inst(flow_var_name, field_abs_offset) + self.vm.set_vm_var_field(flow_var_name, "add_value", add_value) + self.vm.set_vm_var_field(flow_var_name, "is_is_big_endian", is_big_endian) + if (ip_field == "ipv4" and add_checksum_inst): + self.vm.add_fix_checksum_inst(self._pkt_by_hdr.get(ip_layer_name), hdr_offset) + + + def set_vm_eth_range(self, eth_layer_name, eth_field, + mac_init, mac_start, mac_end, add_value, + operation, is_big_endian=False): + if eth_field not in ["src", "dst"]: + raise ValueError("set_vm_eth_range only available for source ('src') or destination ('dst') eth addresses") + + self._verify_layer_prop(eth_layer_name, dpkt.ethernet.Ethernet) + init_val = CTRexPktBuilder._decode_mac_addr(mac_init) + start_val = CTRexPktBuilder._decode_mac_addr(mac_start) + end_val = CTRexPktBuilder._decode_mac_addr(mac_end) + # All validations are done, start adding VM instructions + flow_var_name = "{layer}__{field}".format(layer = eth_layer_name, field = eth_field) + hdr_offset, field_abs_offset = self._calc_offset(eth_layer_name, eth_field) + self.vm.add_flow_man_inst(flow_var_name, size = 8, operation = operation, + init_value = init_val, + min_value = start_val, + max_value = end_val) + self.vm.add_write_flow_inst(flow_var_name, field_abs_offset) + self.vm.set_vm_var_field(flow_var_name, "add_value", add_value) + self.vm.set_vm_var_field(flow_var_name, "is_is_big_endian", is_big_endian) + + def set_vm_custom_range(self, layer_name, hdr_field, + init_val, start_val, end_val, add_val, val_size, + operation, is_big_endian=False, range_name = ""): + self._verify_layer_prop(layer_name=layer_name, field_name=hdr_field) + if not range_name: + range_name = "{layer}__{field}".format(layer = layer_name, field = hdr_field) + hdr_offset, field_abs_offset = self._calc_offset(layer_name, hdr_field) + self.vm.add_flow_man_inst(range_name, size = val_size, operation = operation, + init_value = init_val, + min_value = start_val, + max_value = end_val) + self.vm.add_write_flow_inst(range_name, field_abs_offset) + self.vm.set_vm_var_field(range_name, "add_value", add_val) + self.vm.set_vm_var_field(range_name, "is_is_big_endian", is_big_endian) def get_vm_data(self): pass @@ -374,13 +408,13 @@ class CTRexPktBuilder(object): hdr_offset = len(self._packet) - len(pkt_header) inner_hdr_offsets = [] for field in pkt_header.__hdr__: - if field == hdr_field: + if field[0] == hdr_field: break else: inner_hdr_offsets.append(struct.calcsize(field[1])) - return pkt_header + sum(inner_hdr_offsets) + return hdr_offset, hdr_offset + sum(inner_hdr_offsets) - def _verify_layer_prop(self, layer_name, layer_type = None): + def _verify_layer_prop(self, layer_name, layer_type = None, field_name=None): if layer_name not in self._pkt_by_hdr: raise CTRexPktBuilder.PacketLayerError(layer_name) pkt_layer = self._pkt_by_hdr.get(layer_name) @@ -388,6 +422,12 @@ class CTRexPktBuilder(object): # check for layer type if not isinstance(pkt_layer, layer_type): raise CTRexPktBuilder.PacketLayerTypeError(layer_name, type(pkt_layer), layer_type) + if field_name: + # check if field exists on certain header + if not hasattr(pkt_layer, field_name): + raise CTRexPktBuilder.PacketLayerError(layer_name, "The specified field '{0}' does not exists on " \ + "given packet layer ('{1}')". + format(field_name, layer_name)) return -- cgit 1.2.3-korg From ed46f126c90da83929625a89b5438a11f0e75401 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 24 Sep 2015 02:35:03 +0300 Subject: Cosmetics over packet builder module --- scripts/automation/trex_control_plane/client_utils/packet_builder.py | 1 + 1 file changed, 1 insertion(+) 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 8fd74b1c..9adf7b1d 100644 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -406,6 +406,7 @@ class CTRexPktBuilder(object): def _calc_offset(self, layer_name, hdr_field): pkt_header = self._pkt_by_hdr.get(layer_name) hdr_offset = len(self._packet) - len(pkt_header) + print hdr_offset inner_hdr_offsets = [] for field in pkt_header.__hdr__: if field[0] == hdr_field: -- cgit 1.2.3-korg From 46d15e627e46af8816c061ad33301a8e6a7aab95 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 24 Sep 2015 02:35:34 +0300 Subject: cosmetics... --- .../client_utils/packet_builder.py | 48 ++++++++++------------ 1 file changed, 22 insertions(+), 26 deletions(-) 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 9adf7b1d..b7c456cd 100644 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -260,19 +260,18 @@ class CTRexPktBuilder(object): start_val = CTRexPktBuilder._decode_ip_addr(ip_start, ip_type) end_val = CTRexPktBuilder._decode_ip_addr(ip_end, ip_type) # All validations are done, start adding VM instructions - flow_var_name = "{layer}__{field}".format(layer = ip_layer_name, field = ip_field) + flow_var_name = "{layer}__{field}".format(layer=ip_layer_name, field=ip_field) hdr_offset, field_abs_offset = self._calc_offset(ip_layer_name, ip_field) - self.vm.add_flow_man_inst(flow_var_name, size = ip_addr_size, operation = operation, - init_value = init_val, - min_value = start_val, - max_value = end_val) + self.vm.add_flow_man_inst(flow_var_name, size=ip_addr_size, operation=operation, + init_value=init_val, + min_value=start_val, + max_value=end_val) self.vm.add_write_flow_inst(flow_var_name, field_abs_offset) self.vm.set_vm_var_field(flow_var_name, "add_value", add_value) self.vm.set_vm_var_field(flow_var_name, "is_is_big_endian", is_big_endian) - if (ip_field == "ipv4" and add_checksum_inst): + if ip_field == "ipv4" and add_checksum_inst: self.vm.add_fix_checksum_inst(self._pkt_by_hdr.get(ip_layer_name), hdr_offset) - def set_vm_eth_range(self, eth_layer_name, eth_field, mac_init, mac_start, mac_end, add_value, operation, is_big_endian=False): @@ -284,27 +283,27 @@ class CTRexPktBuilder(object): start_val = CTRexPktBuilder._decode_mac_addr(mac_start) end_val = CTRexPktBuilder._decode_mac_addr(mac_end) # All validations are done, start adding VM instructions - flow_var_name = "{layer}__{field}".format(layer = eth_layer_name, field = eth_field) + flow_var_name = "{layer}__{field}".format(layer=eth_layer_name, field=eth_field) hdr_offset, field_abs_offset = self._calc_offset(eth_layer_name, eth_field) - self.vm.add_flow_man_inst(flow_var_name, size = 8, operation = operation, - init_value = init_val, - min_value = start_val, - max_value = end_val) + self.vm.add_flow_man_inst(flow_var_name, size=8, operation=operation, + init_value=init_val, + min_value=start_val, + max_value=end_val) self.vm.add_write_flow_inst(flow_var_name, field_abs_offset) self.vm.set_vm_var_field(flow_var_name, "add_value", add_value) self.vm.set_vm_var_field(flow_var_name, "is_is_big_endian", is_big_endian) def set_vm_custom_range(self, layer_name, hdr_field, init_val, start_val, end_val, add_val, val_size, - operation, is_big_endian=False, range_name = ""): + operation, is_big_endian=False, range_name=""): self._verify_layer_prop(layer_name=layer_name, field_name=hdr_field) if not range_name: - range_name = "{layer}__{field}".format(layer = layer_name, field = hdr_field) + range_name = "{layer}__{field}".format(layer=layer_name, field=hdr_field) hdr_offset, field_abs_offset = self._calc_offset(layer_name, hdr_field) - self.vm.add_flow_man_inst(range_name, size = val_size, operation = operation, - init_value = init_val, - min_value = start_val, - max_value = end_val) + self.vm.add_flow_man_inst(range_name, size=val_size, operation=operation, + init_value=init_val, + min_value=start_val, + max_value=end_val) self.vm.add_write_flow_inst(range_name, field_abs_offset) self.vm.set_vm_var_field(range_name, "add_value", add_val) self.vm.set_vm_var_field(range_name, "is_is_big_endian", is_big_endian) @@ -406,7 +405,6 @@ class CTRexPktBuilder(object): def _calc_offset(self, layer_name, hdr_field): pkt_header = self._pkt_by_hdr.get(layer_name) hdr_offset = len(self._packet) - len(pkt_header) - print hdr_offset inner_hdr_offsets = [] for field in pkt_header.__hdr__: if field[0] == hdr_field: @@ -415,7 +413,7 @@ class CTRexPktBuilder(object): inner_hdr_offsets.append(struct.calcsize(field[1])) return hdr_offset, hdr_offset + sum(inner_hdr_offsets) - def _verify_layer_prop(self, layer_name, layer_type = None, field_name=None): + def _verify_layer_prop(self, layer_name, layer_type=None, field_name=None): if layer_name not in self._pkt_by_hdr: raise CTRexPktBuilder.PacketLayerError(layer_name) pkt_layer = self._pkt_by_hdr.get(layer_name) @@ -423,15 +421,13 @@ class CTRexPktBuilder(object): # check for layer type if not isinstance(pkt_layer, layer_type): raise CTRexPktBuilder.PacketLayerTypeError(layer_name, type(pkt_layer), layer_type) - if field_name: + if field_name and not hasattr(pkt_layer, field_name): # check if field exists on certain header - if not hasattr(pkt_layer, field_name): - raise CTRexPktBuilder.PacketLayerError(layer_name, "The specified field '{0}' does not exists on " \ - "given packet layer ('{1}')". - format(field_name, layer_name)) + raise CTRexPktBuilder.PacketLayerError(layer_name, "The specified field '{0}' does not exists on " + "given packet layer ('{1}')".format(field_name, + layer_name)) return - @staticmethod def _decode_mac_addr(mac_addr): """ -- cgit 1.2.3-korg From 4651405f6870e31a71b679f30a6ea2d433879d41 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 24 Sep 2015 15:16:20 +0300 Subject: Basically finished (minor touches remaining) all VM instructions implementation --- .../client_utils/packet_builder.py | 106 +++++++++++++-------- 1 file changed, 64 insertions(+), 42 deletions(-) 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 b7c456cd..3a84990c 100644 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -241,72 +241,78 @@ class CTRexPktBuilder(object): # VM access methods def set_vm_ip_range(self, ip_layer_name, ip_field, ip_init, ip_start, ip_end, add_value, - operation, is_big_endian=False, + operation, is_big_endian=False, val_size = 4, ip_type="ipv4", add_checksum_inst=True): if ip_field not in ["src", "dst"]: raise ValueError("set_vm_ip_range only available for source ('src') or destination ('dst') ip addresses") # set differences between IPv4 and IPv6 if ip_type == "ipv4": ip_class = dpkt.ip.IP - ip_addr_size = 4 + ip_addr_size = val_size if val_size <= 4 else 4 elif ip_type == "ipv6": ip_class = dpkt.ip6.IP6 - ip_addr_size = 16 + ip_addr_size = val_size if val_size <= 8 else 4 # TODO: verify what size relevant for ipv6 else: raise CTRexPktBuilder.IPAddressError() self._verify_layer_prop(ip_layer_name, ip_class) - init_val = CTRexPktBuilder._decode_ip_addr(ip_init, ip_type) - start_val = CTRexPktBuilder._decode_ip_addr(ip_start, ip_type) - end_val = CTRexPktBuilder._decode_ip_addr(ip_end, ip_type) + trim_size = ip_addr_size*2 + init_val = str(int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_init, ip_type))[-trim_size:], 16)) + start_val = str(int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_start, ip_type))[-trim_size:], 16)) + end_val = str(int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_end, ip_type))[-trim_size:], 16)) # All validations are done, start adding VM instructions flow_var_name = "{layer}__{field}".format(layer=ip_layer_name, field=ip_field) - hdr_offset, field_abs_offset = self._calc_offset(ip_layer_name, ip_field) + hdr_offset, field_abs_offset = self._calc_offset(ip_layer_name, ip_field, ip_addr_size) self.vm.add_flow_man_inst(flow_var_name, size=ip_addr_size, operation=operation, init_value=init_val, min_value=start_val, max_value=end_val) self.vm.add_write_flow_inst(flow_var_name, field_abs_offset) - self.vm.set_vm_var_field(flow_var_name, "add_value", add_value) - self.vm.set_vm_var_field(flow_var_name, "is_is_big_endian", is_big_endian) - if ip_field == "ipv4" and add_checksum_inst: + self.vm.set_vm_off_inst_field(flow_var_name, "add_value", add_value) + self.vm.set_vm_off_inst_field(flow_var_name, "is_big_endian", is_big_endian) + if ip_type == "ipv4" and add_checksum_inst: self.vm.add_fix_checksum_inst(self._pkt_by_hdr.get(ip_layer_name), hdr_offset) def set_vm_eth_range(self, eth_layer_name, eth_field, mac_init, mac_start, mac_end, add_value, - operation, is_big_endian=False): + operation, val_size = 4, is_big_endian=False): if eth_field not in ["src", "dst"]: raise ValueError("set_vm_eth_range only available for source ('src') or destination ('dst') eth addresses") - self._verify_layer_prop(eth_layer_name, dpkt.ethernet.Ethernet) - init_val = CTRexPktBuilder._decode_mac_addr(mac_init) - start_val = CTRexPktBuilder._decode_mac_addr(mac_start) - end_val = CTRexPktBuilder._decode_mac_addr(mac_end) + eth_addr_size = val_size if val_size <= 4 else 4 + trim_size = eth_addr_size*2 + init_val = str(int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_init))[-trim_size:], 16)) + start_val = str(int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_start))[-trim_size:], 16)) + end_val = str(int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_end))[-trim_size:], 16)) # All validations are done, start adding VM instructions flow_var_name = "{layer}__{field}".format(layer=eth_layer_name, field=eth_field) - hdr_offset, field_abs_offset = self._calc_offset(eth_layer_name, eth_field) + hdr_offset, field_abs_offset = self._calc_offset(eth_layer_name, eth_field, eth_addr_size) self.vm.add_flow_man_inst(flow_var_name, size=8, operation=operation, init_value=init_val, min_value=start_val, max_value=end_val) self.vm.add_write_flow_inst(flow_var_name, field_abs_offset) - self.vm.set_vm_var_field(flow_var_name, "add_value", add_value) - self.vm.set_vm_var_field(flow_var_name, "is_is_big_endian", is_big_endian) + self.vm.set_vm_off_inst_field(flow_var_name, "add_value", add_value) + self.vm.set_vm_off_inst_field(flow_var_name, "is_big_endian", is_big_endian) def set_vm_custom_range(self, layer_name, hdr_field, init_val, start_val, end_val, add_val, val_size, - operation, is_big_endian=False, range_name=""): + operation, is_big_endian=False, range_name="", + add_checksum_inst=True): self._verify_layer_prop(layer_name=layer_name, field_name=hdr_field) if not range_name: range_name = "{layer}__{field}".format(layer=layer_name, field=hdr_field) - hdr_offset, field_abs_offset = self._calc_offset(layer_name, hdr_field) + trim_size = val_size*2 + hdr_offset, field_abs_offset = self._calc_offset(layer_name, hdr_field, val_size) self.vm.add_flow_man_inst(range_name, size=val_size, operation=operation, init_value=init_val, min_value=start_val, max_value=end_val) self.vm.add_write_flow_inst(range_name, field_abs_offset) - self.vm.set_vm_var_field(range_name, "add_value", add_val) - self.vm.set_vm_var_field(range_name, "is_is_big_endian", is_big_endian) + self.vm.set_vm_off_inst_field(range_name, "add_value", add_val) + self.vm.set_vm_off_inst_field(range_name, "is_big_endian", is_big_endian) + if isinstance(self._pkt_by_hdr.get(layer_name), dpkt.ip.IP) and add_checksum_inst: + self.vm.add_fix_checksum_inst(self._pkt_by_hdr.get(layer_name), hdr_offset) def get_vm_data(self): pass @@ -402,13 +408,22 @@ class CTRexPktBuilder(object): if self._pkt_by_hdr[layer] is layer_obj: return layer - def _calc_offset(self, layer_name, hdr_field): + def _calc_offset(self, layer_name, hdr_field, hdr_field_size): pkt_header = self._pkt_by_hdr.get(layer_name) hdr_offset = len(self._packet) - len(pkt_header) inner_hdr_offsets = [] for field in pkt_header.__hdr__: if field[0] == hdr_field: - break + field_size = struct.calcsize(field[1]) + if field_size == hdr_field_size: + break + elif field_size < hdr_field_size: + raise CTRexPktBuilder.PacketLayerError(layer_name, "The specified field '{0}' size is smaller than " + "given range size ('{1}')".format(hdr_field, + hdr_field_size)) + else: + inner_hdr_offsets.append(field_size - hdr_field_size) + break else: inner_hdr_offsets.append(struct.calcsize(field[1])) return hdr_offset, hdr_offset + sum(inner_hdr_offsets) @@ -546,10 +561,10 @@ class CTRexPktBuilder(object): """ super(CTRexPktBuilder.CTRexVM, self).__init__() self.vm_variables = {} - self._inst_by_offset = {} # this data structure holds only offset-related instructions, ordered in tuples - # self.vm_inst_offsets = [] + self._inst_by_offset = {} # this data structure holds only offset-related instructions, ordered in tuples + self._off_inst_by_name = {} - def set_vm_var_field(self, var_name, field_name, val): + def set_vm_var_field(self, var_name, field_name, val, offset_inst=False): """ Set VM variable field. Only existing variables are allowed to be changed. @@ -567,7 +582,13 @@ class CTRexPktBuilder(object): + :exc:`CTRexPktBuilder.VMVarValueError`, in case val isn't one of allowed options of field_name. """ - return self.vm_variables[var_name].set_field(field_name, val) + if offset_inst: + return self._off_inst_by_name[var_name].inst.set_field(field_name, val) + else: + return self.vm_variables[var_name].set_field(field_name, val) + + def set_vm_off_inst_field(self, var_name, field_name, val): + return self.set_vm_var_field(var_name, field_name, val, True) def add_flow_man_inst(self, name, **kwargs): """ @@ -598,19 +619,19 @@ class CTRexPktBuilder(object): else: raise CTRexPktBuilder.VMVarNameExistsError(name) - def add_fix_checksum_inst(self, linked_ipv4_obj, offset_to_obj=20, name=None): + def add_fix_checksum_inst(self, linked_ipv4_obj, offset_to_obj=14, name=None): # check if specified linked_ipv4_obj is indeed an ipv4 object - if not (isinstance(linked_ipv4_obj, dpkt.ip.IP) ): + if not (isinstance(linked_ipv4_obj, dpkt.ip.IP)): raise ValueError("The provided layer object is not of IPv4.") - checksum_offset = offset_to_obj + 10 # IPv4 header places checksum field at the 10-12 Bytes if not name: - name = "checksum_{off}".format(off = checksum_offset) # name will override previous checksum inst, OK - new_checksum_inst = self.CTRexVMChecksumInst(name, checksum_offset) + name = "checksum_{off}".format(off=offset_to_obj) # name will override previous checksum inst, OK + new_checksum_inst = self.CTRexVMChecksumInst(name, offset_to_obj) # store the checksum inst in the end of the IP header (20 Bytes long) - self._inst_by_offset[offset_to_obj + 20] = self.InstStore('checksum', new_checksum_inst) + inst = self.InstStore('checksum', new_checksum_inst) + self._inst_by_offset[offset_to_obj + 20] = inst + self._off_inst_by_name[name] = inst def add_write_flow_inst(self, name, pkt_offset, **kwargs): - pass if name not in self.vm_variables: raise KeyError("Trying to add write_flow_var instruction to a not-exists VM flow variable ('{0}')". format(name)) @@ -620,8 +641,9 @@ class CTRexPktBuilder(object): for (field, value) in kwargs.items(): new_write_inst.set_field(field, value) # add the instruction to the date-structure - self._inst_by_offset[pkt_offset] = self.InstStore('write', new_write_inst) - + inst = self.InstStore('write', new_write_inst) + self._inst_by_offset[pkt_offset] = inst + self._off_inst_by_name[name] = inst def load_flow_man(self, flow_obj): """ @@ -705,7 +727,7 @@ class CTRexPktBuilder(object): self.big_endian = True self.operation = "inc" # self.split_by_core = False - self.init_value = 1 + self.init_value = "1" self.min_value = self.init_value self.max_value = self.init_value @@ -736,8 +758,8 @@ class CTRexPktBuilder(object): elif val not in self.VALID_SIZE: raise CTRexPktBuilder.VMFieldValueError("size", self.VALID_SIZE) elif field_name == "init_value": - if type(val) != int: - raise CTRexPktBuilder.VMFieldTypeError("init_value", int) + if type(val) != str: + raise CTRexPktBuilder.VMFieldTypeError("init_value", str) elif field_name == "operation": if type(val) != str: raise CTRexPktBuilder.VMFieldTypeError("operation", str) @@ -759,7 +781,7 @@ class CTRexPktBuilder(object): dictionary holds variable data of VM variable """ - return {"ins_name": "flow_man_simple", # VM variable dump always refers to manipulate instruction. + return {"ins_name": "flow_var", # VM variable dump always refers to manipulate instruction. "name": self.name, "size": self.size, "op": self.operation, @@ -805,7 +827,7 @@ class CTRexPktBuilder(object): raise CTRexPktBuilder.VMFieldNameError(field_name) elif field_name == 'pkt_offset': raise ValueError("pkt_offset value cannot be changed") - cur_attr_type = getattr(self, field_name) + cur_attr_type = type(getattr(self, field_name)) if cur_attr_type == type(val): setattr(self, field_name, val) else: -- cgit 1.2.3-korg From 2af9cd559d9c86e42824e920b5d9168584216e86 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 4 Oct 2015 17:19:52 +0300 Subject: Updated .gitignore file to avoid _build compiled documentation --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5f5dc3a8..73ce5afe 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ scripts/_t-rex-* scripts/bp-sim-* scripts/doc/* scripts/mock-* - +scripts/automation/trex_control_plane/doc/_build/* *.pyc -- cgit 1.2.3-korg From 7e1bf3602241fb881531965813c0b6ad09790bee Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 4 Oct 2015 17:21:14 +0300 Subject: Replaced "outer_packages" with "external_packages" to avoid naming collision --- .../client_utils/external_packages.py | 30 +++++++++++++++++++++ .../client_utils/jsonrpc_client.py | 2 +- .../client_utils/outer_packages.py | 31 ---------------------- 3 files changed, 31 insertions(+), 32 deletions(-) create mode 100755 scripts/automation/trex_control_plane/client_utils/external_packages.py mode change 100644 => 100755 scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py delete mode 100644 scripts/automation/trex_control_plane/client_utils/outer_packages.py diff --git a/scripts/automation/trex_control_plane/client_utils/external_packages.py b/scripts/automation/trex_control_plane/client_utils/external_packages.py new file mode 100755 index 00000000..f8de0323 --- /dev/null +++ b/scripts/automation/trex_control_plane/client_utils/external_packages.py @@ -0,0 +1,30 @@ +#!/router/bin/python + +import sys +import site +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 = ['zmq', + 'dpkt-1.8.6' + ] + +def import_client_utils_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) + site.addsitedir(full_path) + +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 old mode 100644 new mode 100755 index c6b22218..ebeec77e --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -1,6 +1,6 @@ #!/router/bin/python -import outer_packages +import external_packages import zmq import json import general_utils diff --git a/scripts/automation/trex_control_plane/client_utils/outer_packages.py b/scripts/automation/trex_control_plane/client_utils/outer_packages.py deleted file mode 100644 index a6c9a2eb..00000000 --- a/scripts/automation/trex_control_plane/client_utils/outer_packages.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/router/bin/python - -import sys -import site -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 = ['zmq', - 'dpkt-1.8.6' - ] - - -def import_client_utils_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) - site.addsitedir(full_path) - -import_client_utils_modules() - -- cgit 1.2.3-korg From 16e3b19016a0d6e63d9162040811de4386142af0 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 4 Oct 2015 17:22:46 +0300 Subject: Updated and added documentation files for packet builder module --- .../trex_control_plane/doc/client_utils.rst | 31 +-- .../automation/trex_control_plane/doc/index.rst | 32 ++- .../doc/packet_generator/examples.rst | 233 +++++++++++++++++++++ .../doc/packet_generator/export_format.yaml | 27 +++ .../doc/packet_generator/index.rst | 17 ++ .../doc/packet_generator/packet_builder_code.rst | 12 ++ .../doc/packet_generator/stream_export.rst | 27 +++ 7 files changed, 356 insertions(+), 23 deletions(-) create mode 100755 scripts/automation/trex_control_plane/doc/packet_generator/examples.rst create mode 100755 scripts/automation/trex_control_plane/doc/packet_generator/export_format.yaml create mode 100755 scripts/automation/trex_control_plane/doc/packet_generator/index.rst create mode 100755 scripts/automation/trex_control_plane/doc/packet_generator/packet_builder_code.rst create mode 100755 scripts/automation/trex_control_plane/doc/packet_generator/stream_export.rst diff --git a/scripts/automation/trex_control_plane/doc/client_utils.rst b/scripts/automation/trex_control_plane/doc/client_utils.rst index 224dfe19..32728a57 100755 --- a/scripts/automation/trex_control_plane/doc/client_utils.rst +++ b/scripts/automation/trex_control_plane/doc/client_utils.rst @@ -1,14 +1,19 @@ - -Client Utilities -================ - -T-Rex YAML generator --------------------- - -.. automodule:: trex_yaml_gen - :members: - -General Utilities ------------------ -.. automodule:: general_utils + +Client Utilities +================ + +.. toctree:: + :maxdepth: 2 + + packet_generator/index + +TRex YAML generator +------------------- + +.. automodule:: trex_yaml_gen + :members: + +General Utilities +----------------- +.. automodule:: general_utils :members: \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/index.rst b/scripts/automation/trex_control_plane/doc/index.rst index e7a619d8..ebcd5dcf 100755 --- a/scripts/automation/trex_control_plane/doc/index.rst +++ b/scripts/automation/trex_control_plane/doc/index.rst @@ -1,18 +1,18 @@ -.. T-Rex Control Plain documentation master file, created by +.. TRex Control Plain documentation master file, created by sphinx-quickstart on Tue Jun 2 07:48:10 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to T-Rex Control Plain's documentation! -=============================================== +Welcome to TRex Control Plain's documentation! +============================================== -T-Rex is a **realistic traffic generator** that enables you to do get learn more about your under developement devices. +TRex is a **realistic traffic generator** that enables you to do get learn more about your under development devices. -This site covers the Python API of T-Rex control plane, and explains how to utilize it to your needs. +This site covers the Python API of TRex control plane, and explains how to utilize it to your needs. However, since the entire API is JSON-RPC [#f1]_ based, you may want to check out other implementations that could suit you. -To understand the entirely how the API works and how to set up the server side, check out the `API documentation `_ undee the documentation section of T-Rex website. +To understand the entirely how the API works and how to set up the server side, check out the `trex-core Wiki `_ under the documentation section of TRex website. **Use the table of contents below or the menu to your left to navigate through the site** @@ -24,8 +24,6 @@ Getting Started :maxdepth: 2 installation - client_utils - usage_examples API Reference ============= @@ -34,8 +32,22 @@ API Reference api/index -About T-Rex -=========== +Client Utilities +================ +.. toctree:: + :maxdepth: 2 + + client_utils + +Usage Examples +============== +.. toctree:: + :maxdepth: 2 + + usage_examples + +About TRex +========== .. toctree:: :maxdepth: 2 diff --git a/scripts/automation/trex_control_plane/doc/packet_generator/examples.rst b/scripts/automation/trex_control_plane/doc/packet_generator/examples.rst new file mode 100755 index 00000000..f903feac --- /dev/null +++ b/scripts/automation/trex_control_plane/doc/packet_generator/examples.rst @@ -0,0 +1,233 @@ + +Packet Builder Usage Examples +============================= + +Whenever TRex is publishing live data, it uses JSON notation to describe the data-object. + +Each client may parse it diffrently, however this page will describe the values meaning when published by TRex server. + + +Main Fields +----------- + +Each TRex server-published JSON object contains data divided to main fields under which the actual data lays. + +These main fields are: + ++-----------------------------+----------------------------------------------------+---------------------------+ +| Main field | Contains | Comments | ++=============================+====================================================+===========================+ +| :ref:`trex-global-field` | Must-have data on TRex run, | | +| | mainly regarding Tx/Rx and packet drops | | ++-----------------------------+----------------------------------------------------+---------------------------+ +| :ref:`tx-gen-field` | Data indicate the quality of the transmit process. | | +| | In case histogram is zero it means that all packets| | +| | were injected in the right time. | | ++-----------------------------+----------------------------------------------------+---------------------------+ +| :ref:`trex-latecny-field` | Latency reports, containing latency data on | - Generated when latency | +| | generated data and on response traffic | test is enabled (``l`` | +| | | param) | +| | | - *typo* on field key: | ++-----------------------------+----------------------------------------------------+ will be fixed on next | +| :ref:`trex-latecny-v2-field`| Extended latency information | release | ++-----------------------------+----------------------------------------------------+---------------------------+ + + +Each of these fields contains keys for field general data (such as its name) and its actual data, which is always stored under the **"data"** key. + +For example, in order to access some trex-global data, the access path would look like:: + + AllData -> trex-global -> data -> desired_info + + + + +Detailed explanation +-------------------- + +.. _trex-global-field: + +trex-global field +~~~~~~~~~~~~~~~~~ + + ++--------------------------------+-------+-----------------------------------------------------------+ +| Sub-key | Type | Meaning | ++================================+=======+===========================================================+ +| m_cpu_util | float | CPU utilization (0-100) | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_platform_factor | float | multiplier factor | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_tx_bps | float | total tx bit per second | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_rx_bps | float | total rx bit per second | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_tx_pps | float | total tx packet per second | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_tx_cps | float | total tx connection per second | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_tx_expected_cps | float | expected tx connection per second | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_tx_expected_pps | float | expected tx packet per second | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_tx_expected_bps | float | expected tx bit per second | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_rx_drop_bps | float | drop rate in bit per second | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_active_flows | float | active trex flows | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_open_flows | float | open trex flows from startup (monotonically incrementing) | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_total_tx_pkts | int | total tx in packets | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_total_rx_pkts | int | total rx in packets | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_total_tx_bytes | int | total tx in bytes | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_total_rx_bytes | int | total rx in bytes | ++--------------------------------+-------+-----------------------------------------------------------+ +| opackets-# | int | output packets (per interface) | ++--------------------------------+-------+-----------------------------------------------------------+ +| obytes-# | int | output bytes (per interface) | ++--------------------------------+-------+-----------------------------------------------------------+ +| ipackets-# | int | input packet (per interface) | ++--------------------------------+-------+-----------------------------------------------------------+ +| ibytes-# | int | input bytes (per interface) | ++--------------------------------+-------+-----------------------------------------------------------+ +| ierrors-# | int | input errors (per interface) | ++--------------------------------+-------+-----------------------------------------------------------+ +| oerrors-# | int | input errors (per interface) | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_total_tx_bps-# | float | total transmitted data in bit per second | ++--------------------------------+-------+-----------------------------------------------------------+ +| unknown | int | | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_total_nat_learn_error [#f1]_ | int | | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_total_nat_active [#f2]_ | int | | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_total_nat_no_fid [#f2]_ | int | | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_total_nat_time_out [#f2]_ | int | | ++--------------------------------+-------+-----------------------------------------------------------+ +| m_total_nat_open [#f2]_ | int | | ++--------------------------------+-------+-----------------------------------------------------------+ + + +.. _tx-gen-field: + +tx-gen field +~~~~~~~~~~~~~~ + ++-------------------+-------+-----------------------------------------------------------+ +| Sub-key | Type | Meaning | ++===================+=======+===========================================================+ +| realtime-hist | dict | histogram of transmission. See extended information about | +| | | histogram object under :ref:`histogram-object-fields`. | +| | | The attribute analyzed is time packet has been sent | +| | | before/after it was intended to be | ++-------------------+-------+-----------------------------------------------------------+ +| unknown | int | | ++-------------------+-------+-----------------------------------------------------------+ + +.. _trex-latecny-field: + +trex-latecny field +~~~~~~~~~~~~~~~~~~ + ++---------+-------+---------------------------------------------------------+ +| Sub-key | Type | Meaning | ++=========+=======+=========================================================+ +| avg-# | float | average latency in usec (per interface) | ++---------+-------+---------------------------------------------------------+ +| max-# | float | max latency in usec from the test start (per interface) | ++---------+-------+---------------------------------------------------------+ +| c-max-# | float | max in the last 1 sec window (per interface) | ++---------+-------+---------------------------------------------------------+ +| error-# | float | errors in latency packets (per interface) | ++---------+-------+---------------------------------------------------------+ +| unknown | int | | ++---------+-------+---------------------------------------------------------+ + +.. _trex-latecny-v2-field: + +trex-latecny-v2 field +~~~~~~~~~~~~~~~~~~~~~ + ++--------------------------------------+-------+--------------------------------------+ +| Sub-key | Type | Meaning | ++======================================+=======+======================================+ +| cpu_util | float | rx thread cpu % (this is not trex DP | +| | | threads cpu%%) | ++--------------------------------------+-------+--------------------------------------+ +| port-# | | Containing per interface | +| | dict | information. See extended | +| | | information under ``port-# -> | +| | | key_name -> sub_key`` | ++--------------------------------------+-------+--------------------------------------+ +| port-#->hist | dict | histogram of latency. See extended | +| | | information about histogram object | +| | | under :ref:`histogram-object-fields`.| ++--------------------------------------+-------+--------------------------------------+ +| port-#->stats | | Containing per interface | +| | dict | information. See extended | +| | | information under ``port-# -> | +| | | key_name -> sub_key`` | ++--------------------------------------+-------+--------------------------------------+ +| port-#->stats->m_tx_pkt_ok | int | total of try sent packets | ++--------------------------------------+-------+--------------------------------------+ +| port-#->stats->m_pkt_ok | int | total of packets sent from hardware | ++--------------------------------------+-------+--------------------------------------+ +| port-#->stats->m_no_magic | int | rx error with no magic | ++--------------------------------------+-------+--------------------------------------+ +| port-#->stats->m_no_id | int | rx errors with no id | ++--------------------------------------+-------+--------------------------------------+ +| port-#->stats->m_seq_error | int | error in seq number | ++--------------------------------------+-------+--------------------------------------+ +| port-#->stats->m_length_error | int | | ++--------------------------------------+-------+--------------------------------------+ +| port-#->stats->m_rx_check | int | packets tested in rx | ++--------------------------------------+-------+--------------------------------------+ +| unknown | int | | ++--------------------------------------+-------+--------------------------------------+ + + + +.. _histogram-object-fields: + +Histogram object fields +~~~~~~~~~~~~~~~~~~~~~~~ + +The histogram object is being used in number of place throughout the JSON object. +The following section describes its fields in detail. + + ++-----------+-------+-----------------------------------------------------------------------------------+ +| Sub-key | Type | Meaning | ++===========+=======+===================================================================================+ +| min_usec | int | min attribute value in usec. pkt with latency less than this value is not counted | ++-----------+-------+-----------------------------------------------------------------------------------+ +| max_usec | int | max attribute value in usec | ++-----------+-------+-----------------------------------------------------------------------------------+ +| high_cnt | int | how many packets on which its attribute > min_usec | ++-----------+-------+-----------------------------------------------------------------------------------+ +| cnt | int | total packets from test startup | ++-----------+-------+-----------------------------------------------------------------------------------+ +| s_avg | float | average value from test startup | ++-----------+-------+-----------------------------------------------------------------------------------+ +| histogram | | histogram of relevant object by the following keys: | +| | array | - key: value in usec | +| | | - val: number of packets | ++-----------+-------+-----------------------------------------------------------------------------------+ + + +Access Examples +--------------- + + + +.. rubric:: Footnotes + +.. [#f1] Available only in NAT and NAT learning operation (``learn`` and ``learn-verify`` flags) + +.. [#f2] Available only in NAT operation (``learn`` flag) \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/packet_generator/export_format.yaml b/scripts/automation/trex_control_plane/doc/packet_generator/export_format.yaml new file mode 100755 index 00000000..86ae4bc1 --- /dev/null +++ b/scripts/automation/trex_control_plane/doc/packet_generator/export_format.yaml @@ -0,0 +1,27 @@ +router: + model : 1RU + hostname : ASR1001_T-Rex + ip_address : 10.56.199.247 + image : asr1001-universalk9.BLD_V155_2_S_XE315_THROTTLE_LATEST_20150121_110036-std.bin + line_password : lab + en_password : lab + mgmt_interface : GigabitEthernet0/0/0 + clean_config : /Configurations/danklei/asr1001_TRex_clean_config.cfg + intf_masking : 255.255.255.0 + ipv6_mask : 64 + interfaces : + - client : + name : GigabitEthernet0/0/1 + src_mac_addr : 0000.0001.0000 + dest_mac_addr : 0000.0001.0000 + server : + name : GigabitEthernet0/0/2 + src_mac_addr : 0000.0001.0000 + dest_mac_addr : 0000.0001.0000 + vrf_name : null + +tftp: + hostname : ats-asr-srv-1 + ip_address : 10.56.128.23 + root_dir : /auto/avc-devtest/ + images_path : /images/1RU/ \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/packet_generator/index.rst b/scripts/automation/trex_control_plane/doc/packet_generator/index.rst new file mode 100755 index 00000000..f3cf46ff --- /dev/null +++ b/scripts/automation/trex_control_plane/doc/packet_generator/index.rst @@ -0,0 +1,17 @@ + +TRex Packet Builder +------------------- +The TRex Packet Generator is a module designed to generate single-packet and set its ranging options, later to be transmitted using TRex. + +The packet generator module does extensive usage with `dkpt `_ python module to create packet headers. + +.. toctree:: + :maxdepth: 4 + + packet_builder_code + +.. toctree:: + :maxdepth: 0 + + examples + stream_export \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/packet_generator/packet_builder_code.rst b/scripts/automation/trex_control_plane/doc/packet_generator/packet_builder_code.rst new file mode 100755 index 00000000..3a6e8d5f --- /dev/null +++ b/scripts/automation/trex_control_plane/doc/packet_generator/packet_builder_code.rst @@ -0,0 +1,12 @@ + +CTRexPktBuilder class +--------------------- + +.. autoclass:: packet_builder.CTRexPktBuilder + :members: + :member-order: bysource + +Packet Builder Exceptions +------------------------- + +For exceptions documentation see here: :exc:`Packet Builder Exceptions ` \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/packet_generator/stream_export.rst b/scripts/automation/trex_control_plane/doc/packet_generator/stream_export.rst new file mode 100755 index 00000000..8d5ad5be --- /dev/null +++ b/scripts/automation/trex_control_plane/doc/packet_generator/stream_export.rst @@ -0,0 +1,27 @@ + +Stream Export YAML syntax +========================= + +In order to provide a fluent work-flow that utilize the best TRex user's time, an export-import mini language has been created. + +This enables a work-flow that supports saving and sharing a built packets and its scenarios, so that other tools +(such as TRex Console) could use them. + +The TRex Packet Builder module supports (using ___ method) the export of built stream according to the format described below. + +Guidelines +---------- + +1. One +2. Two +3. Three + +Export Format +------------- + +.. literalinclude:: export_format.yaml + +Example +------- + +Whenever TRex is publishing live data, it uses JSON notation to describe the data-object. -- cgit 1.2.3-korg From e4484262bbe2566fc1123e399504627a8757fb20 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 4 Oct 2015 17:23:27 +0300 Subject: Updated sshpass file to avoid creation of daemon processes --- scripts/automation/sshpass.exp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/automation/sshpass.exp b/scripts/automation/sshpass.exp index f27210c8..3b5ce560 100755 --- a/scripts/automation/sshpass.exp +++ b/scripts/automation/sshpass.exp @@ -12,4 +12,6 @@ match_max 100000 expect "*?assword:*" send -- "$pass\r" send -- "\r" -interact +expect eof +wait +#interact -- cgit 1.2.3-korg From 1c43d1c6eb10ccff6b26fac6d3588bb5a1439997 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 4 Oct 2015 17:24:02 +0300 Subject: Updated implementation for packet_builder module --- .../client_utils/packet_builder.py | 107 +++++++++++++++------ 1 file changed, 79 insertions(+), 28 deletions(-) mode change 100644 => 100755 scripts/automation/trex_control_plane/client_utils/packet_builder.py diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py old mode 100644 new mode 100755 index 3a84990c..30aede0b --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -1,6 +1,6 @@ #!/router/bin/python -import outer_packages +import external_packages import dpkt import socket import binascii @@ -251,15 +251,15 @@ class CTRexPktBuilder(object): ip_addr_size = val_size if val_size <= 4 else 4 elif ip_type == "ipv6": ip_class = dpkt.ip6.IP6 - ip_addr_size = val_size if val_size <= 8 else 4 # TODO: verify what size relevant for ipv6 + ip_addr_size = val_size if val_size <= 8 else 4 else: raise CTRexPktBuilder.IPAddressError() self._verify_layer_prop(ip_layer_name, ip_class) trim_size = ip_addr_size*2 - init_val = str(int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_init, ip_type))[-trim_size:], 16)) - start_val = str(int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_start, ip_type))[-trim_size:], 16)) - end_val = str(int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_end, ip_type))[-trim_size:], 16)) + init_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_init, ip_type))[-trim_size:], 16) + start_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_start, ip_type))[-trim_size:], 16) + end_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_end, ip_type))[-trim_size:], 16) # All validations are done, start adding VM instructions flow_var_name = "{layer}__{field}".format(layer=ip_layer_name, field=ip_field) hdr_offset, field_abs_offset = self._calc_offset(ip_layer_name, ip_field, ip_addr_size) @@ -281,9 +281,9 @@ class CTRexPktBuilder(object): self._verify_layer_prop(eth_layer_name, dpkt.ethernet.Ethernet) eth_addr_size = val_size if val_size <= 4 else 4 trim_size = eth_addr_size*2 - init_val = str(int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_init))[-trim_size:], 16)) - start_val = str(int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_start))[-trim_size:], 16)) - end_val = str(int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_end))[-trim_size:], 16)) + init_val = int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_init))[-trim_size:], 16) + start_val = int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_start))[-trim_size:], 16) + end_val = int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_end))[-trim_size:], 16) # All validations are done, start adding VM instructions flow_var_name = "{layer}__{field}".format(layer=eth_layer_name, field=eth_field) hdr_offset, field_abs_offset = self._calc_offset(eth_layer_name, eth_field, eth_addr_size) @@ -418,9 +418,9 @@ class CTRexPktBuilder(object): if field_size == hdr_field_size: break elif field_size < hdr_field_size: - raise CTRexPktBuilder.PacketLayerError(layer_name, "The specified field '{0}' size is smaller than " - "given range size ('{1}')".format(hdr_field, - hdr_field_size)) + raise CTRexPktBuilder.PacketLayerError(layer_name, + "The specified field '{0}' size is smaller than given range" + " size ('{1}')".format(hdr_field, hdr_field_size)) else: inner_hdr_offsets.append(field_size - hdr_field_size) break @@ -599,7 +599,7 @@ class CTRexPktBuilder(object): name of the manipulation, must be distinct. Example: 'source_ip_change' - **kwargs : dict + **kwargs** : dict optional, set flow_man fields on initialization (key = field_name, val = field_val). Must be used with legit fields, see :func:`CTRexPktBuilder.CTRexVM.CTRexVMVariable.set_field`. @@ -727,7 +727,7 @@ class CTRexPktBuilder(object): self.big_endian = True self.operation = "inc" # self.split_by_core = False - self.init_value = "1" + self.init_value = 1 self.min_value = self.init_value self.max_value = self.init_value @@ -757,9 +757,9 @@ class CTRexPktBuilder(object): raise CTRexPktBuilder.VMFieldTypeError("size", int) elif val not in self.VALID_SIZE: raise CTRexPktBuilder.VMFieldValueError("size", self.VALID_SIZE) - elif field_name == "init_value": - if type(val) != str: - raise CTRexPktBuilder.VMFieldTypeError("init_value", str) + elif field_name in ["init_value", "min_value", "max_value"]: + if type(val) != int: + raise CTRexPktBuilder.VMFieldTypeError(field_name, int) elif field_name == "operation": if type(val) != str: raise CTRexPktBuilder.VMFieldTypeError("operation", str) @@ -786,9 +786,60 @@ class CTRexPktBuilder(object): "size": self.size, "op": self.operation, # "split_by_core": self.split_by_core, - "init_value": self.init_value, - "min_value": self.min_value, - "max_value": self.max_value} + "init_value": str(self.init_value), + "min_value": str(self.min_value), + "max_value": str(self.max_value)} + + class CTRexVMChecksumInst(CVMAbstractInstruction): + + def __init__(self, name, offset): + """ + Instantiate a CTRexVMChecksumInst object + + :parameters: + name : str + a string representing the name of the VM variable. + """ + super(CTRexPktBuilder.CTRexVM.CTRexVMChecksumInst, self).__init__(name) + self.pkt_offset = offset + + def dump(self): + return {"type": "fix_checksum_ipv4", + "pkt_offset": int(self.pkt_offset)} + + class CTRexVMWrtFlowVarInst(CVMAbstractInstruction): + + def __init__(self, name, pkt_offset): + """ + Instantiate a CTRexVMWrtFlowVarInst object + + :parameters: + name : str + a string representing the name of the VM variable. + """ + super(CTRexPktBuilder.CTRexVM.CTRexVMWrtFlowVarInst, self).__init__(name) + self.pkt_offset = int(pkt_offset) + self.add_value = 0 + self.is_big_endian = False + + def set_field(self, field_name, val): + if not hasattr(self, field_name): + raise CTRexPktBuilder.VMFieldNameError(field_name) + elif field_name == 'pkt_offset': + raise ValueError("pkt_offset value cannot be changed") + cur_attr_type = type(getattr(self, field_name)) + if cur_attr_type == type(val): + setattr(self, field_name, val) + else: + CTRexPktBuilder.VMFieldTypeError(field_name, cur_attr_type) + + def dump(self): + return {"type": "write_flow_var", + "name": self.name, + "pkt_offset": self.pkt_offset, + "add_value": int(self.add_value), + "is_big_endian": bool(self.is_big_endian) + } class CTRexVMChecksumInst(CVMAbstractInstruction): @@ -896,8 +947,8 @@ class CTRexPktBuilder(object): This exception is used to indicate an error caused by operation performed on an non-exists layer of the packet. """ def __init__(self, name, layer_type, ok_type, message=''): - self._default_message = 'The type of packet layer {layer_name} is of type {layer_type}, \ - and not of the expected {allowed_type}.'.format(layer_name=name, + self._default_message = "The type of packet layer {layer_name} is of type {layer_type}, " \ + "and not of the expected {allowed_type}.".format(layer_name=name, layer_type=layer_type, allowed_type=ok_type.__name__) self.message = message or self._default_message @@ -926,10 +977,10 @@ class CTRexPktBuilder(object): This exception is used to indicate an illegal value has type has been given to VM variable field. """ def __init__(self, name, ok_type, message=''): - self._default_message = 'The desired value of field {field_name} is of type {field_type}, \ - and not of the allowed {allowed_type}.'.format(field_name=name, - field_type=type(name).__name__, - allowed_type=ok_type.__name__) + self._default_message = "The desired value of field {field_name} is of type {field_type}, " \ + "and not of the allowed {allowed_type}.".format(field_name=name, + field_type=type(name).__name__, + allowed_type=ok_type.__name__) self.message = message or self._default_message super(CTRexPktBuilder.VMFieldTypeError, self).__init__(-31, self.message) @@ -938,9 +989,9 @@ class CTRexPktBuilder(object): This exception is used to indicate an error an illegal value has been assigned to VM variable field. """ def __init__(self, name, ok_opts, message=''): - self._default_message = 'The desired value of field {field_name} is illegal.\n \ - The only allowed options are: {allowed_opts}.'.format(field_name=name, - allowed_opts=ok_opts) + self._default_message = "The desired value of field {field_name} is illegal.\n" \ + "The only allowed options are: {allowed_opts}.".format(field_name=name, + allowed_opts=ok_opts) self.message = message or self._default_message super(CTRexPktBuilder.VMFieldValueError, self).__init__(-32, self.message) -- cgit 1.2.3-korg From c7ba20f4f7c4521fdf50238c7e4ccc50f13b248e Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 4 Oct 2015 18:39:11 +0300 Subject: Updated packet export doc --- .../doc/packet_generator/export_format.yaml | 72 ++++++++++++++-------- .../doc/packet_generator/index.rst | 1 + .../doc/packet_generator/stream_export.rst | 10 +-- 3 files changed, 53 insertions(+), 30 deletions(-) diff --git a/scripts/automation/trex_control_plane/doc/packet_generator/export_format.yaml b/scripts/automation/trex_control_plane/doc/packet_generator/export_format.yaml index 86ae4bc1..9f8c8f7b 100755 --- a/scripts/automation/trex_control_plane/doc/packet_generator/export_format.yaml +++ b/scripts/automation/trex_control_plane/doc/packet_generator/export_format.yaml @@ -1,27 +1,47 @@ -router: - model : 1RU - hostname : ASR1001_T-Rex - ip_address : 10.56.199.247 - image : asr1001-universalk9.BLD_V155_2_S_XE315_THROTTLE_LATEST_20150121_110036-std.bin - line_password : lab - en_password : lab - mgmt_interface : GigabitEthernet0/0/0 - clean_config : /Configurations/danklei/asr1001_TRex_clean_config.cfg - intf_masking : 255.255.255.0 - ipv6_mask : 64 - interfaces : - - client : - name : GigabitEthernet0/0/1 - src_mac_addr : 0000.0001.0000 - dest_mac_addr : 0000.0001.0000 - server : - name : GigabitEthernet0/0/2 - src_mac_addr : 0000.0001.0000 - dest_mac_addr : 0000.0001.0000 - vrf_name : null +#################################################### +#### TRex packet export format #### +#################################################### -tftp: - hostname : ats-asr-srv-1 - ip_address : 10.56.128.23 - root_dir : /auto/avc-devtest/ - images_path : /images/1RU/ \ No newline at end of file +# PACKET REP - OPTION #1 +packet: + is_pcap : YES/NO # <1> + binary : [] # <2> + pcap : path/to/pcap/file.pcap # <3> + meta : any metadata wished to # <4> + +# PACKET REP - OPTION #2 +packet: + data : [] / path/to/pcap/file.pcap # <5> + meta : any metadata wished to # <4> + +vm: # <6> + - vm instruction #1 + - vm instruction #2 + ... + - vm instruction #N + + +################################### +#### Comments #### +################################### +# +# <1>: is_pcap is a boolean field that indicates if packet is transferred by pcap referencs +# ('YES') or binary representation ('NO'). +# +# <2>: binary field encodes the packet in binary representation. in a sequence (array) data. +# Each array item is an integer ranging 0-255. +# **LEAVE BLANK IF USING PCAP REFERENCE** +# +# <3>: path to the linked pcap file. Make sure to provide path with reading credentials. +# **LEAVE BLANK IF USING BINARY REP FOR THE PACKET** +# +# <4>: meta data is any JSON formatted data ment to be passed on. +# +# <5>: data field can be both binary representation or pcap file refernce, +# without the need for user's explicit typing. +# The application logic differs between the cases by the object type +# (array/string ending in '.pcap') +# Less configuration, little more confusing, LESS similar to RPC spec +# +# <6>: vm instructions passed in array representation (sequence). +# Each instruction is deifned according to the structures of the supported VM instructions. \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/packet_generator/index.rst b/scripts/automation/trex_control_plane/doc/packet_generator/index.rst index f3cf46ff..ed1d460d 100755 --- a/scripts/automation/trex_control_plane/doc/packet_generator/index.rst +++ b/scripts/automation/trex_control_plane/doc/packet_generator/index.rst @@ -12,6 +12,7 @@ The packet generator module does extensive usage with `dkpt `_ of TRex RPC server spec, which defines the raw interaction with TRex server. +3. Only packet binary data and VM instructinos are to be saved. Any meta-data packet builder module used while creating the packet will be stripped out. Export Format ------------- .. literalinclude:: export_format.yaml + :lines: 4- + :linenos: Example ------- -Whenever TRex is publishing live data, it uses JSON notation to describe the data-object. +The following files snapshot represents each of the options. -- cgit 1.2.3-korg From 9629c9953516281d4bdaad1ed63d145de336a983 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 6 Oct 2015 07:28:24 +0300 Subject: Commiting last night progress --- .../client/trex_stateless_client.py | 300 +++++++++++++-------- .../client_utils/packet_builder.py | 118 +++++++- 2 files changed, 292 insertions(+), 126 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 12f6eff6..b7580531 100644 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -16,135 +16,66 @@ class CTRexStatelessClient(object): def __init__(self, server="localhost", port=5050, virtual=False): super(CTRexStatelessClient, self).__init__() self.tx_link = CTRexStatelessClient.CTxLink(server, port, virtual) + self._conn_handler = {} - def add_stream(self, handler, port_id, stream_id, stream_obj): - assert isinstance(stream_obj, CTRexStatelessClient.CStream) - params = {"handler":handler, + def owned(func): + def wrapper(self, *args, **kwargs ) : + if self._conn_handler.get(kwargs.get("port_id")): + return func(self, *args, **kwargs) + else: + raise RuntimeError("The requested method ('{0}') cannot be invoked unless the desired port is owned". + format(func.__name__)) + return wrapper + + def acquire(self, port_id, username, force=False): + params = {"port_id": port_id, + "user": username, + "force": force} + self._conn_handler[port_id] = self.transmit("acquire", params) + return self._conn_handler + + @owned + def release(self, port_id=None): + self._conn_handler.pop(port_id) + params = {"handler":self._conn_handler.get(port_id), + "port_id": port_id} + return self.transmit("release", params) + + @owned + def add_stream(self, stream_id, stream_obj, port_id=None): + assert isinstance(stream_obj, CStream) + params = {"handler":self._conn_handler.get(port_id), "port_id":port_id, "stream_id":stream_id, "stream":stream_obj.dump()} return self.transmit("add_stream", params) - def transmit(self, method_name, params={}): - return self.tx_link.transmit(method_name, params) + @owned + def remove_stream(self, stream_id, port_id=None): + params = {"handler":self._conn_handler.get(port_id), + "port_id":port_id, + "stream_id":stream_id} + return self.transmit("remove_stream", params) + @owned + def get_stream_list(self, port_id=None): + params = {"handler":self._conn_handler.get(port_id), + "port_id":port_id} + return self.transmit("get_stream_list", params) - # ------ private classes ------ # - class CStream(object): - """docstring for CStream""" - def __init__(self): - super(CTRexStatelessClient.CStream, self).__init__() - self.packet = CTRexPktBuilder() - self.rx_stats = CTRexStatelessClient.CRxStats() - self.mode = CTRexStatelessClient.CTxMode() - self.isg - self._next_stream = -1 - self._self_start - self._enabled - - @property - def enabled(self): - return self._enabled - - @enabled.setter - def enabled(self, bool_value): - self._enabled = bool(bool_value) - - @property - def self_start(self): - return self._self_start - - @self_start.setter - def self_start(self, bool_value): - self._self_start = bool(bool_value) - - @property - def next_stream(self): - return self._next_stream - - @next_stream.setter - def next_stream(self, value): - self._next_stream = int(bool_value) - - def dump(self): - pass - return {"enabled":self.enabled, - "self_start":self.self_start, - "isg":self.isg, - "next_stream":self.next_stream, - "packet":self.packet.dump_pkt(), - "mode":self.mode.dump(), - "vm":self.packet.dump_vm_instructions(), # TODO - add this method to packet builder module - "rx_stats":self.rx_stats.dump()} - - class CRxStats(object): - - def __init__(self, enabled=False, seq_enabled=False, latency_enabled=False): - self._rx_dict = {"enabled" : enabled, - "seq_enabled" : seq_enabled, - "latency_enabled" : latency_enabled} - - @property - def enabled(self): - return self._rx_dict.get("enabled") - - @enabled.setter - def enabled(self, bool_value): - self._rx_dict['enabled'] = bool(bool_value) - - @property - def seq_enabled(self): - return self._rx_dict.get("seq_enabled") - - @seq_enabled.setter - def seq_enabled(self, bool_value): - self._rx_dict['seq_enabled'] = bool(bool_value) - - @property - def latency_enabled(self): - return self._rx_dict.get("latency_enabled") - - @latency_enabled.setter - def latency_enabled(self, bool_value): - self._rx_dict['latency_enabled'] = bool(bool_value) - - def dump(self): - return {k:v - for k,v in self._rx_dict.items() - if v - } - - class CTxMode(object): - """docstring for CTxMode""" - def __init__(self, tx_mode, pps): - super(CTxMode, self).__init__() - if tx_mode not in ["continuous", "single_burst", "multi_burst"]: - raise ValueError("Unknown TX mode ('{0}')has been initialized.".format(tx_mode)) - self._tx_mode = tx_mode - self._fields = {'pps':float(pps)} - if tx_mode == "single_burst": - self._fields['total_pkts'] = 0 - elif tx_mode == "multi_burst": - self._fields['pkts_per_burst'] = 0 - self._fields['ibg'] = 0.0 - self._fields['count'] = 0 - else: - pass + @owned + def get_stream(self, stream_id, port_id=None): + params = {"handler":self._conn_handler.get(port_id), + "port_id":port_id} + return self.transmit("get_stream_list", params) - def set_tx_mode_attr(self, attr, val): - if attr in self._fields: - self._fields[attr] = type(self._fields.get(attr))(val) - else: - raise ValueError("The provided attribute ('{0}') is not a legal attribute in selected TX mode ('{1}')". - format(attr, self._tx_mode)) - def dump(self): - dump = {"type":self._tx_mode} - dump.update({k:v - for k, v in self._fields.items() - }) - return dump + def transmit(self, method_name, params={}): + return self.tx_link.transmit(method_name, params) + + + # ------ private classes ------ # class CTxLink(object): """describes the connectivity of the stateless client method""" def __init__(self, server="localhost", port=5050, virtual=False): @@ -167,7 +98,140 @@ class CTRexStatelessClient(object): else: return self.rpc_link.invoke_rpc_method(method_name, params) +class CStream(object): + """docstring for CStream""" + DEFAULTS = {"rx_stats": CRxStats, + "mode": CTxMode, + "isg": 5.0, + "next_stream": -1, + "self_start": True, + "enabled": True} + + def __init__(self, **kwargs): + super(CStream, self).__init__() + for k, v in kwargs.items(): + setattr(self, k, v) + # set default values to unset attributes, according to DEFAULTS dict + set_keys = set(kwargs.keys()) + keys_to_set = [x for x in self.DEFAULTS if x not in set_keys] + for key in keys_to_set: + default = self.DEFAULTS.get(key) + if type(default)==type: + setattr(self, key, default()) + else: + setattr(self, key, default) + + @property + def packet(self): + return self._packet + + @packet.setter + def packet(self, packet_obj): + assert isinstance(packet_obj, CTRexPktBuilder) + self._packet = packet_obj + + @property + def enabled(self): + return self._enabled + + @enabled.setter + def enabled(self, bool_value): + self._enabled = bool(bool_value) + + @property + def self_start(self): + return self._self_start + + @self_start.setter + def self_start(self, bool_value): + self._self_start = bool(bool_value) + + @property + def next_stream(self): + return self._next_stream + + @next_stream.setter + def next_stream(self, value): + self._next_stream = int(value) + + def dump(self): + pass + return {"enabled":self.enabled, + "self_start":self.self_start, + "isg":self.isg, + "next_stream":self.next_stream, + "packet":self.packet.dump_pkt(), + "mode":self.mode.dump(), + "vm":self.packet.get_vm_data(), + "rx_stats":self.rx_stats.dump()} + +class CRxStats(object): + + def __init__(self, enabled=False, seq_enabled=False, latency_enabled=False): + self._rx_dict = {"enabled" : enabled, + "seq_enabled" : seq_enabled, + "latency_enabled" : latency_enabled} + + @property + def enabled(self): + return self._rx_dict.get("enabled") + + @enabled.setter + def enabled(self, bool_value): + self._rx_dict['enabled'] = bool(bool_value) + + @property + def seq_enabled(self): + return self._rx_dict.get("seq_enabled") + + @seq_enabled.setter + def seq_enabled(self, bool_value): + self._rx_dict['seq_enabled'] = bool(bool_value) + + @property + def latency_enabled(self): + return self._rx_dict.get("latency_enabled") + + @latency_enabled.setter + def latency_enabled(self, bool_value): + self._rx_dict['latency_enabled'] = bool(bool_value) + + def dump(self): + return {k:v + for k,v in self._rx_dict.items() + if v + } + +class CTxMode(object): + """docstring for CTxMode""" + def __init__(self, tx_mode, pps): + super(CTxMode, self).__init__() + if tx_mode not in ["continuous", "single_burst", "multi_burst"]: + raise ValueError("Unknown TX mode ('{0}')has been initialized.".format(tx_mode)) + self._tx_mode = tx_mode + self._fields = {'pps':float(pps)} + if tx_mode == "single_burst": + self._fields['total_pkts'] = 0 + elif tx_mode == "multi_burst": + self._fields['pkts_per_burst'] = 0 + self._fields['ibg'] = 0.0 + self._fields['count'] = 0 + else: + pass + def set_tx_mode_attr(self, attr, val): + if attr in self._fields: + self._fields[attr] = type(self._fields.get(attr))(val) + else: + raise ValueError("The provided attribute ('{0}') is not a legal attribute in selected TX mode ('{1}')". + format(attr, self._tx_mode)) + + def dump(self): + dump = {"type":self._tx_mode} + dump.update({k:v + for k, v in self._fields.items() + }) + return dump if __name__ == "__main__": 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 30aede0b..0505d7f1 100755 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -64,6 +64,36 @@ class CTRexPktBuilder(object): return def set_ip_layer_addr(self, layer_name, attr, ip_addr, ip_type="ipv4"): + """ + This method sets the IP address fields of an IP header (source or destination, for both IPv4 and IPv6) + using a human readable addressing representation. + + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l3_ip", etc. + + attr: str + a string representation of the sub-field to be set: + + "src" for source + + "dst" for destination + + ip_addr: str + a string representation of the IP address to be set. + Example: "10.0.0.1" for IPv4, or "5001::DB8:1:3333:1:1" for IPv6 + + ip_type : str + a string representation of the IP version to be set: + + "ipv4" for IPv4 + + "ipv6" for IPv6 + + Default: **ipv4** + + :raises: + + :exc:`ValueError`, in case the desired layer_name is not an IP layer + + :exc:`KeyError`, in case the desired layer_name does not exists. + + """ try: layer = self._pkt_by_hdr[layer_name.lower()] if not (isinstance(layer, dpkt.ip.IP) or isinstance(layer, dpkt.ip6.IP6)): @@ -75,9 +105,54 @@ class CTRexPktBuilder(object): raise KeyError("Specified layer '{0}' doesn't exist on packet.".format(layer_name)) def set_ipv6_layer_addr(self, layer_name, attr, ip_addr): + """ + This method sets the IPv6 address fields of an IP header (source or destination) + + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l3_ip", etc. + + attr: str + a string representation of the sub-field to be set: + + "src" for source + + "dst" for destination + + ip_addr: str + a string representation of the IP address to be set. + Example: "5001::DB8:1:3333:1:1" + + :raises: + + :exc:`ValueError`, in case the desired layer_name is not an IPv6 layer + + :exc:`KeyError`, in case the desired layer_name does not exists. + + """ self.set_ip_layer_addr(layer_name, attr, ip_addr, ip_type="ipv6") def set_eth_layer_addr(self, layer_name, attr, mac_addr): + """ + This method sets the ethernet address fields of an Ethernet header (source or destination) + using a human readable addressing representation. + + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l2", etc. + + attr: str + a string representation of the sub-field to be set: + + "src" for source + + "dst" for destination + + mac_addr: str + a string representation of the MAC address to be set. + Example: "00:de:34:ef:2e:f4". + + :raises: + + :exc:`ValueError`, in case the desired layer_name is not an Ethernet layer + + :exc:`KeyError`, in case the desired layer_name does not exists. + + """ try: layer = self._pkt_by_hdr[layer_name.lower()] if not isinstance(layer, dpkt.ethernet.Ethernet): @@ -136,6 +211,29 @@ class CTRexPktBuilder(object): raise KeyError("Specified layer '{0}' doesn't exist on packet.".format(layer_name)) def set_layer_bit_attr(self, layer_name, attr, val): + """ + This method enables the user to set the value of a field smaller that 1 Byte in size. + This method isn't used to set full-sized fields value (>= 1 byte). + Use :func:`packet_builder.CTRexPktBuilder.set_layer_attr` instead. + + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l2", "l4_tcp", etc. + + attr : str + a string representing the attribute to be set on desired layer + + val : int + value of attribute. + This value will be set "ontop" of the existing value using bitwise "OR" operation. + .. tip:: It is very useful to use dpkt constants to define the values of these fields. + + :raises: + + :exc:`KeyError`, in case of missing layer (the desired layer isn't part of packet) + + :exc:`ValueError`, in case invalid attribute to the specified layer. + + """ return self.set_layer_attr(layer_name, attr, val, True) def set_pkt_payload(self, payload): @@ -241,7 +339,7 @@ class CTRexPktBuilder(object): # VM access methods def set_vm_ip_range(self, ip_layer_name, ip_field, ip_init, ip_start, ip_end, add_value, - operation, is_big_endian=False, val_size = 4, + operation, is_big_endian=False, val_size=4, ip_type="ipv4", add_checksum_inst=True): if ip_field not in ["src", "dst"]: raise ValueError("set_vm_ip_range only available for source ('src') or destination ('dst') ip addresses") @@ -275,7 +373,7 @@ class CTRexPktBuilder(object): def set_vm_eth_range(self, eth_layer_name, eth_field, mac_init, mac_start, mac_end, add_value, - operation, val_size = 4, is_big_endian=False): + operation, val_size=4, is_big_endian=False): if eth_field not in ["src", "dst"]: raise ValueError("set_vm_eth_range only available for source ('src') or destination ('dst') eth addresses") self._verify_layer_prop(eth_layer_name, dpkt.ethernet.Ethernet) @@ -299,15 +397,20 @@ class CTRexPktBuilder(object): init_val, start_val, end_val, add_val, val_size, operation, is_big_endian=False, range_name="", add_checksum_inst=True): + # verify input validity for init/start/end values + for val in [init_val, start_val, end_val]: + if not isinstance(val, int): + raise ValueError("init/start/end values are expected integers, but received type '{0}'". + format(type(val))) self._verify_layer_prop(layer_name=layer_name, field_name=hdr_field) if not range_name: range_name = "{layer}__{field}".format(layer=layer_name, field=hdr_field) trim_size = val_size*2 hdr_offset, field_abs_offset = self._calc_offset(layer_name, hdr_field, val_size) self.vm.add_flow_man_inst(range_name, size=val_size, operation=operation, - init_value=init_val, - min_value=start_val, - max_value=end_val) + init_value=str(init_val), + min_value=str(start_val), + max_value=str(end_val)) self.vm.add_write_flow_inst(range_name, field_abs_offset) self.vm.set_vm_off_inst_field(range_name, "add_value", add_val) self.vm.set_vm_off_inst_field(range_name, "is_big_endian", is_big_endian) @@ -315,7 +418,7 @@ class CTRexPktBuilder(object): self.vm.add_fix_checksum_inst(self._pkt_by_hdr.get(layer_name), hdr_offset) def get_vm_data(self): - pass + return self.vm.dump() def dump_pkt(self): """ @@ -369,8 +472,7 @@ class CTRexPktBuilder(object): except IOError: raise IOError(2, "The provided path could not be accessed") - # ----- useful shortcut methods ----- # - def gen_dns_packet(self): + def export_pkt(self, file_path, link_pcap=False, pcap_name=None, pcap_ts=None): pass # ----- internal methods ----- # -- cgit 1.2.3-korg From ada8c62d7393e22cee7fccf9eb1b16b8ebe3c7c8 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 6 Oct 2015 01:53:24 +0300 Subject: Removed any "T-Rex" string out of the code and documentation, using GREP! --- .../trex_control_plane/client/trex_client.py | 140 ++++++++++----------- .../client_utils/general_utils.py | 2 +- .../client_utils/trex_yaml_gen.py | 8 +- .../trex_control_plane/common/trex_exceptions.py | 34 ++--- .../trex_control_plane/common/trex_status_e.py | 2 +- 5 files changed, 93 insertions(+), 93 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_client.py b/scripts/automation/trex_control_plane/client/trex_client.py index 56775766..c3677132 100755 --- a/scripts/automation/trex_control_plane/client/trex_client.py +++ b/scripts/automation/trex_control_plane/client/trex_client.py @@ -30,18 +30,18 @@ from distutils.util import strtobool class CTRexClient(object): """ - This class defines the client side of the RESTfull interaction with T-Rex + This class defines the client side of the RESTfull interaction with TRex """ def __init__(self, trex_host, max_history_size = 100, trex_daemon_port = 8090, trex_zmq_port = 4500, verbose = False): """ - Instantiate a T-Rex client object, and connecting it to listening daemon-server + Instantiate a TRex client object, and connecting it to listening daemon-server :parameters: trex_host : str - a string of the t-rex ip address or hostname. + a string of the TRex ip address or hostname. max_history_size : int - a number to set the maximum history size of a single T-Rex run. Each sampling adds a new item to history. + a number to set the maximum history size of a single TRex run. Each sampling adds a new item to history. default value : **100** trex_daemon_port : int @@ -69,7 +69,7 @@ class CTRexClient(object): self.result_obj = CTRexResult(max_history_size) self.decoder = JSONDecoder() self.trex_server_path = "http://{hostname}:{port}/".format( hostname = trex_host, port = trex_daemon_port ) - self.__verbose_print("Connecting to T-Rex @ {trex_path} ...".format( trex_path = self.trex_server_path ) ) + self.__verbose_print("Connecting to TRex @ {trex_path} ...".format( trex_path = self.trex_server_path ) ) self.history = jsonrpclib.history.History() self.server = jsonrpclib.Server(self.trex_server_path, history = self.history) self.check_server_connectivity() @@ -90,7 +90,7 @@ class CTRexClient(object): def start_trex (self, f, d, block_to_success = True, timeout = 30, user = None, **trex_cmd_options): """ - Request to start a T-Rex run on server. + Request to start a TRex run on server. :parameters: f : str @@ -98,17 +98,17 @@ class CTRexClient(object): d : int the desired duration of the test. must be at least 30 seconds long. block_to_success : bool - determine if this method blocks until T-Rex changes state from 'Starting' to either 'Idle' or 'Running' + determine if this method blocks until TRex changes state from 'Starting' to either 'Idle' or 'Running' default value : **True** timeout : int - maximum time (in seconds) to wait in blocking state until T-Rex changes state from 'Starting' to either 'Idle' or 'Running' + maximum time (in seconds) to wait in blocking state until TRex changes state from 'Starting' to either 'Idle' or 'Running' default value: **30** user : str the identity of the the run issuer. trex_cmd_options : key, val - sets desired T-Rex options using key=val syntax, separated by comma. + sets desired TRex options using key=val syntax, separated by comma. for keys with no value, state key=True :return: @@ -117,8 +117,8 @@ class CTRexClient(object): :raises: + :exc:`ValueError`, in case 'd' parameter inserted with wrong value. + :exc:`trex_exceptions.TRexError`, in case one of the trex_cmd_options raised an exception at server. - + :exc:`trex_exceptions.TRexInUseError`, in case T-Rex is already taken. - + :exc:`trex_exceptions.TRexRequestDenied`, in case T-Rex is reserved for another user than the one trying start T-Rex. + + :exc:`trex_exceptions.TRexInUseError`, in case TRex is already taken. + + :exc:`trex_exceptions.TRexRequestDenied`, in case TRex is reserved for another user than the one trying start TRex. + ProtocolError, in case of error in JSON-RPC protocol. """ @@ -128,7 +128,7 @@ class CTRexClient(object): if d < 30: # specify a test should take at least 30 seconds long. raise ValueError except ValueError: - raise ValueError('d parameter must be integer, specifying how long T-Rex run, and must be larger than 30 secs.') + raise ValueError('d parameter must be integer, specifying how long TRex run, and must be larger than 30 secs.') trex_cmd_options.update( {'f' : f, 'd' : d} ) @@ -146,25 +146,25 @@ class CTRexClient(object): if retval!=0: self.seq = retval # update seq num only on successful submission return True - else: # T-Rex is has been started by another user - raise TRexInUseError('T-Rex is already being used by another user or process. Try again once T-Rex is back in IDLE state.') + else: # TRex is has been started by another user + raise TRexInUseError('TRex is already being used by another user or process. Try again once TRex is back in IDLE state.') def stop_trex (self): """ - Request to stop a T-Rex run on server. + Request to stop a TRex run on server. - The request is only valid if the stop initiator is the same client as the T-Rex run initiator. + The request is only valid if the stop initiator is the same client as the TRex run initiator. :parameters: None :return: + **True** on successful termination - + **False** if request issued but T-Rex wasn't running. + + **False** if request issued but TRex wasn't running. :raises: - + :exc:`trex_exceptions.TRexRequestDenied`, in case T-Rex ir running but started by another user. - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed T-Rex run (unexpected termination). + + :exc:`trex_exceptions.TRexRequestDenied`, in case TRex ir running but started by another user. + + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). + ProtocolError, in case of error in JSON-RPC protocol. """ @@ -179,16 +179,16 @@ class CTRexClient(object): def force_kill (self, confirm = True): """ - Force killing of running T-Rex process (if exists) on the server. + Force killing of running TRex process (if exists) on the server. .. tip:: This method is a safety method and **overrides any running or reserved resources**, and as such isn't designed to be used on a regular basis. Always consider using :func:`trex_client.CTRexClient.stop_trex` instead. - In the end of this method, T-Rex will return to IDLE state with no reservation. + In the end of this method, TRex will return to IDLE state with no reservation. :parameters: confirm : bool - Prompt a user confirmation before continue terminating T-Rex session + Prompt a user confirmation before continue terminating TRex session :return: + **True** on successful termination @@ -199,7 +199,7 @@ class CTRexClient(object): """ if confirm: - prompt = "WARNING: This will terminate active T-Rex session indiscriminately.\nAre you sure? " + prompt = "WARNING: This will terminate active TRex session indiscriminately.\nAre you sure? " sys.stdout.write('%s [y/n]\n' % prompt) while True: try: @@ -221,20 +221,20 @@ class CTRexClient(object): def wait_until_kickoff_finish(self, timeout = 40): """ - Block the client application until T-Rex changes state from 'Starting' to either 'Idle' or 'Running' + Block the client application until TRex changes state from 'Starting' to either 'Idle' or 'Running' - The request is only valid if the stop initiator is the same client as the T-Rex run initiator. + The request is only valid if the stop initiator is the same client as the TRex run initiator. :parameters: timeout : int - maximum time (in seconds) to wait in blocking state until T-Rex changes state from 'Starting' to either 'Idle' or 'Running' + maximum time (in seconds) to wait in blocking state until TRex changes state from 'Starting' to either 'Idle' or 'Running' :return: + **True** on successful termination - + **False** if request issued but T-Rex wasn't running. + + **False** if request issued but TRex wasn't running. :raises: - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed T-Rex run (unexpected termination). + + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). + ProtocolError, in case of error in JSON-RPC protocol. .. note:: Exceptions are throws only when start_trex did not block in the first place, i.e. `block_to_success` parameter was set to `False` @@ -252,22 +252,22 @@ class CTRexClient(object): def is_running (self, dump_out = False): """ - Poll for T-Rex running status. + Poll for TRex running status. - If T-Rex is running, a history item will be added into result_obj and processed. + If TRex is running, a history item will be added into result_obj and processed. - .. tip:: This method is especially useful for iterating until T-Rex run is finished. + .. tip:: This method is especially useful for iterating until TRex run is finished. :parameters: dump_out : dict if passed, the pointer object is cleared and the latest dump stored in it. :return: - + **True** if T-Rex is running. - + **False** if T-Rex is not running. + + **True** if TRex is running. + + **False** if TRex is not running. :raises: - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed T-Rex run (unexpected termination). + + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). + :exc:`TypeError`, in case JSON stream decoding error. + ProtocolError, in case of error in JSON-RPC protocol. @@ -292,7 +292,7 @@ class CTRexClient(object): def get_trex_files_path (self): """ - Fetches the local path in which files are stored when pushed to t-rex server from client. + Fetches the local path in which files are stored when pushed to TRex server from client. :parameters: None @@ -300,7 +300,7 @@ class CTRexClient(object): :return: string representation of the desired path - .. note:: The returned path represents a path on the T-Rex server **local machine** + .. note:: The returned path represents a path on the TRex server **local machine** :raises: ProtocolError, in case of error in JSON-RPC protocol. @@ -317,7 +317,7 @@ class CTRexClient(object): def get_running_status (self): """ - Fetches the current T-Rex status. + Fetches the current TRex status. If available, a verbose data will accompany the state itself. @@ -344,18 +344,18 @@ class CTRexClient(object): def get_running_info (self): """ - Performs single poll of T-Rex running data and process it into the result object (named `result_obj`). + Performs single poll of TRex running data and process it into the result object (named `result_obj`). - .. tip:: This method will throw an exception if T-Rex isn't running. Always consider using :func:`trex_client.CTRexClient.is_running` which handles a single poll operation in safer manner. + .. tip:: This method will throw an exception if TRex isn't running. Always consider using :func:`trex_client.CTRexClient.is_running` which handles a single poll operation in safer manner. :parameters: None :return: - dictionary containing the most updated data dump from T-Rex. + dictionary containing the most updated data dump from TRex. :raises: - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed T-Rex run (unexpected termination). + + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). + :exc:`TypeError`, in case JSON stream decoding error. + ProtocolError, in case of error in JSON-RPC protocol. @@ -379,7 +379,7 @@ class CTRexClient(object): def sample_until_condition (self, condition_func, time_between_samples = 5): """ - Automatically sets ongoing sampling of T-Rex data, with sampling rate described by time_between_samples. + Automatically sets ongoing sampling of TRex data, with sampling rate described by time_between_samples. On each fetched dump, the condition_func is applied on the result objects, and if returns True, the sampling will stop. @@ -394,36 +394,36 @@ class CTRexClient(object): default value : **5** :return: - the first result object (see :class:`CTRexResult` for further details) of the T-Rex run on which the condition has been met. + the first result object (see :class:`CTRexResult` for further details) of the TRex run on which the condition has been met. :raises: + :exc:`UserWarning`, in case the condition_func method condition hasn't been met - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed T-Rex run (unexpected termination). + + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). + :exc:`TypeError`, in case JSON stream decoding error. + ProtocolError, in case of error in JSON-RPC protocol. + :exc:`Exception`, in case the condition_func suffered from any kind of exception """ - # make sure T-Rex is running. raise exceptions here if any + # make sure TRex is running. raise exceptions here if any self.wait_until_kickoff_finish() try: while self.is_running(): results = self.get_result_obj() if condition_func(results): - # if condition satisfied, stop T-Rex and return result object + # if condition satisfied, stop TRex and return result object self.stop_trex() return results time.sleep(time_between_samples) except TRexWarning: # means we're back to Idle state, and didn't meet our condition - raise UserWarning("T-Rex results condition wasn't met during T-Rex run.") + raise UserWarning("TRex results condition wasn't met during TRex run.") except Exception: # this could come from provided method 'condition_func' raise def sample_to_run_finish (self, time_between_samples = 5): """ - Automatically sets automatically sampling of T-Rex data with sampling rate described by time_between_samples until T-Rex run finished. + Automatically sets automatically sampling of TRex data with sampling rate described by time_between_samples until TRex run finished. :parameters: time_between_samples : int @@ -436,7 +436,7 @@ class CTRexClient(object): :raises: + :exc:`UserWarning`, in case the condition_func method condition hasn't been met - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed T-Rex run (unexpected termination). + + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). + :exc:`TypeError`, in case JSON stream decoding error. + ProtocolError, in case of error in JSON-RPC protocol. @@ -475,13 +475,13 @@ class CTRexClient(object): def is_reserved (self): """ - Checks if T-Rex is currently reserved to any user or not. + Checks if TRex is currently reserved to any user or not. :parameters: None :return: - + **True** if T-Rex is reserved. + + **True** if TRex is reserved. + **False** otherwise. :raises: @@ -499,13 +499,13 @@ class CTRexClient(object): def reserve_trex (self, user = None): """ - Reserves the usage of T-Rex to a certain user. + Reserves the usage of TRex to a certain user. - When T-Rex is reserved, it can't be reserved. + When TRex is reserved, it can't be reserved. :parameters: user : str - a username of the desired owner of T-Rex + a username of the desired owner of TRex default: current logged user @@ -513,8 +513,8 @@ class CTRexClient(object): **True** if reservation made successfully :raises: - + :exc:`trex_exceptions.TRexRequestDenied`, in case T-Rex is reserved for another user than the one trying to make the reservation. - + :exc:`trex_exceptions.TRexInUseError`, in case T-Rex is currently running. + + :exc:`trex_exceptions.TRexRequestDenied`, in case TRex is reserved for another user than the one trying to make the reservation. + + :exc:`trex_exceptions.TRexInUseError`, in case TRex is currently running. + ProtocolError, in case of error in JSON-RPC protocol. """ @@ -530,14 +530,14 @@ class CTRexClient(object): def cancel_reservation (self, user = None): """ - Cancels a current reservation of T-Rex to a certain user. + Cancels a current reservation of TRex to a certain user. - When T-Rex is reserved, no other user can start new T-Rex runs. + When TRex is reserved, no other user can start new TRex runs. :parameters: user : str - a username of the desired owner of T-Rex + a username of the desired owner of TRex default: current logged user @@ -546,7 +546,7 @@ class CTRexClient(object): + **False** if there was no reservation at all. :raises: - + :exc:`trex_exceptions.TRexRequestDenied`, in case T-Rex is reserved for another user than the one trying to cancel the reservation. + + :exc:`trex_exceptions.TRexRequestDenied`, in case TRex is reserved for another user than the one trying to cancel the reservation. + ProtocolError, in case of error in JSON-RPC protocol. """ @@ -627,7 +627,7 @@ class CTRexClient(object): return method_to_call() except socket.error as e: if e.errno == errno.ECONNREFUSED: - raise SocketError(errno.ECONNREFUSED, "Connection from T-Rex server was refused. Please make sure the server is up.") + raise SocketError(errno.ECONNREFUSED, "Connection from TRex server was refused. Please make sure the server is up.") def check_server_connectivity (self): """ @@ -640,7 +640,7 @@ class CTRexClient(object): raise socket.gaierror(e.errno, "Could not resolve server hostname. Please make sure hostname entered correctly.") except socket.error as e: if e.errno == errno.ECONNREFUSED: - raise socket.error(errno.ECONNREFUSED, "Connection from T-Rex server was refused. Please make sure the server is up.") + raise socket.error(errno.ECONNREFUSED, "Connection from TRex server was refused. Please make sure the server is up.") finally: self.prompt_verbose_data() @@ -671,7 +671,7 @@ class CTRexClient(object): def _handle_AppError_exception(self, err): """ - This private method triggres the T-Rex dedicated exception generation in case a general ProtocolError has been raised. + This private method triggres the TRex dedicated exception generation in case a general ProtocolError has been raised. """ # handle known exceptions based on known error codes. # if error code is not known, raise ProtocolError @@ -680,17 +680,17 @@ class CTRexClient(object): class CTRexResult(object): """ - A class containing all results received from T-Rex. + A class containing all results received from TRex. Ontop to containing the results, this class offers easier data access and extended results processing options """ def __init__(self, max_history_size): """ - Instatiate a T-Rex result object + Instatiate a TRex result object :parameters: max_history_size : int - a number to set the maximum history size of a single T-Rex run. Each sampling adds a new item to history. + a number to set the maximum history size of a single TRex run. Each sampling adds a new item to history. """ self._history = deque(maxlen = max_history_size) @@ -749,7 +749,7 @@ class CTRexResult(object): def get_avg_latency (self): """ - Fetches the average latency measured on each of the interfaces from the start of T-Rex run + Fetches the average latency measured on each of the interfaces from the start of TRex run :parameters: None @@ -779,7 +779,7 @@ class CTRexResult(object): def get_total_drops (self): """ - Fetches the total number of drops identified from the moment T-Rex run began. + Fetches the total number of drops identified from the moment TRex run began. :parameters: None @@ -835,7 +835,7 @@ class CTRexResult(object): def is_done_warmup (self): """ - Checks if T-Rex latest results TX-rate indicates that T-Rex has reached its expected TX-rate. + Checks if TRex latest results TX-rate indicates that TRex has reached its expected TX-rate. :parameters: None @@ -856,7 +856,7 @@ class CTRexResult(object): defines a path to desired data. .. tip:: | Use '.' to enter one level deeper in dictionary hierarchy. - | Use '[i]' to access the i'th indexed obejct of an array. + | Use '[i]' to access the i'th indexed object of an array. tree_path_to_key : regex apply a regex to filter results out from a multiple results set. diff --git a/scripts/automation/trex_control_plane/client_utils/general_utils.py b/scripts/automation/trex_control_plane/client_utils/general_utils.py index b5912628..5488b9dd 100755 --- a/scripts/automation/trex_control_plane/client_utils/general_utils.py +++ b/scripts/automation/trex_control_plane/client_utils/general_utils.py @@ -37,7 +37,7 @@ def find_path_to_pardir (pardir, base_path = os.getcwd() ): """ Finds the absolute path for some parent dir `pardir`, starting from base_path - The request is only valid if the stop intitiator is the same client as the T-Rex run intitiator. + The request is only valid if the stop initiator is the same client as the TRex run initiator. :parameters: pardir : str diff --git a/scripts/automation/trex_control_plane/client_utils/trex_yaml_gen.py b/scripts/automation/trex_control_plane/client_utils/trex_yaml_gen.py index 755674ea..c26fef29 100755 --- a/scripts/automation/trex_control_plane/client_utils/trex_yaml_gen.py +++ b/scripts/automation/trex_control_plane/client_utils/trex_yaml_gen.py @@ -7,7 +7,7 @@ import os class CTRexYaml(object): """ - This class functions as a YAML generator according to T-Rex YAML format. + This class functions as a YAML generator according to TRex YAML format. CTRexYaml is compatible with both Python 2 and Python 3. """ @@ -38,7 +38,7 @@ class CTRexYaml(object): :parameters: trex_files_path : str - a path (on T-Rex server side) for the pcap files using which T-Rex can access it. + a path (on TRex server side) for the pcap files using which TRex can access it. """ self.yaml_obj = list(CTRexYaml.YAML_TEMPLATE) @@ -114,7 +114,7 @@ class CTRexYaml(object): :parameters: None - :reaturn: + :return: None """ @@ -193,7 +193,7 @@ class CTRexYaml(object): """ Returns a list of all files related to the YAML object, including the YAML filename itself. - .. tip:: This method is especially useful for listing all the files that should be pushed to T-Rex server as part of the same yaml selection. + .. tip:: This method is especially useful for listing all the files that should be pushed to TRex server as part of the same yaml selection. :parameters: None diff --git a/scripts/automation/trex_control_plane/common/trex_exceptions.py b/scripts/automation/trex_control_plane/common/trex_exceptions.py index 1353fd00..0de38411 100755 --- a/scripts/automation/trex_control_plane/common/trex_exceptions.py +++ b/scripts/automation/trex_control_plane/common/trex_exceptions.py @@ -27,7 +27,7 @@ class RPCError(Exception): class TRexException(RPCError): """ - This is the most general T-Rex exception. + This is the most general TRex exception. All exceptions inherits from this class has an error code and a default message which describes the most common use case of the error. @@ -35,55 +35,55 @@ class TRexException(RPCError): """ code = -10 - _default_message = 'T-Rex encountered an unexpected error. please contact T-Rex dev team.' + _default_message = 'TRex encountered an unexpected error. please contact TRex dev team.' # api_name = 'TRex' class TRexError(TRexException): """ - This is the most general T-Rex exception. + This is the most general TRex exception. This exception isn't used by default and will only when an unrelated to ProtocolError will occur, and it can't be resolved to any of the deriviate exceptions. """ code = -11 - _default_message = 'T-Rex run failed due to wrong input parameters, or due to reachability issues.' + _default_message = 'TRex run failed due to wrong input parameters, or due to reachability issues.' class TRexWarning(TRexException): - """ Indicates a warning from T-Rex server. When this exception raises it normally used to indicate required data isn't ready yet """ + """ Indicates a warning from TRex server. When this exception raises it normally used to indicate required data isn't ready yet """ code = -12 - _default_message = 'T-Rex is starting (data is not available yet).' + _default_message = 'TRex is starting (data is not available yet).' class TRexRequestDenied(TRexException): """ Indicates the desired reques was denied by the server """ code = -33 - _default_message = 'T-Rex desired request denied because the requested resource is already taken. Try again once T-Rex is back in IDLE state.' + _default_message = 'TRex desired request denied because the requested resource is already taken. Try again once TRex is back in IDLE state.' class TRexInUseError(TRexException): """ - Indicates that T-Rex is currently in use + Indicates that TRex is currently in use """ code = -13 - _default_message = 'T-Rex is already being used by another user or process. Try again once T-Rex is back in IDLE state.' + _default_message = 'TRex is already being used by another user or process. Try again once TRex is back in IDLE state.' class TRexRunFailedError(TRexException): - """ Indicates that T-Rex has failed due to some reason. This Exception is used when T-Rex process itself terminates due to unknown reason """ + """ Indicates that TRex has failed due to some reason. This Exception is used when TRex process itself terminates due to unknown reason """ code = -14 _default_message = '' class TRexIncompleteRunError(TRexException): """ - Indicates that T-Rex has failed due to some reason. - This Exception is used when T-Rex process itself terminated with error fault or it has been terminated by an external intervention in the OS. + Indicates that TRex has failed due to some reason. + This Exception is used when TRex process itself terminated with error fault or it has been terminated by an external intervention in the OS. """ code = -15 - _default_message = 'T-Rex run was terminated unexpectedly by outer process or by the hosting OS' + _default_message = 'TRex run was terminated unexpectedly by outer process or by the hosting OS' EXCEPTIONS = [TRexException, TRexError, TRexWarning, TRexInUseError, TRexRequestDenied, TRexRunFailedError, TRexIncompleteRunError] class CExceptionHandler(object): """ - CExceptionHandler is responsible for generating T-Rex API related exceptions in client side. + CExceptionHandler is responsible for generating TRex API related exceptions in client side. """ def __init__(self, exceptions): """ @@ -92,7 +92,7 @@ class CExceptionHandler(object): :parameters: exceptions : list - a list of all T-Rex acceptable exception objects. + a list of all TRex acceptable exception objects. default list: - :exc:`trex_exceptions.TRexException` @@ -113,7 +113,7 @@ class CExceptionHandler(object): """ Generates an exception based on a general ProtocolError exception object `err`. - When T-Rex is reserved, no other user can start new T-Rex runs. + When TRex is reserved, no other user can start new TRex runs. :parameters: @@ -122,7 +122,7 @@ class CExceptionHandler(object): a ProtocolError exception raised by :class:`trex_client.CTRexClient` class :return: - A T-Rex exception from the exception list defined in class creation. + A TRex exception from the exception list defined in class creation. If such exception wasn't found, returns a TRexException exception diff --git a/scripts/automation/trex_control_plane/common/trex_status_e.py b/scripts/automation/trex_control_plane/common/trex_status_e.py index 34db9b39..3ad85014 100755 --- a/scripts/automation/trex_control_plane/common/trex_status_e.py +++ b/scripts/automation/trex_control_plane/common/trex_status_e.py @@ -4,5 +4,5 @@ from enum import Enum -# define the states in which a T-Rex can hold during its lifetime +# define the states in which a TRex can hold during its lifetime TRexStatus = Enum('TRexStatus', 'Idle Starting Running') -- cgit 1.2.3-korg From 25c528e867b13d8ddaee19f208ddedd8a2e505ca Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 6 Oct 2015 01:54:49 +0300 Subject: removed any T-Rex string in doc --- .../trex_control_plane/doc/about_trex.rst | 21 ++++++++++++--------- .../automation/trex_control_plane/doc/authors.rst | 12 ------------ scripts/automation/trex_control_plane/doc/conf.py | 14 +++++++------- scripts/automation/trex_control_plane/doc/index.rst | 4 +--- .../trex_control_plane/doc/installation.rst | 10 +++++----- .../trex_control_plane/doc/json_dictionary.yaml | 6 +++--- .../automation/trex_control_plane/doc/license.rst | 18 ------------------ .../trex_control_plane/doc/usage_examples.rst | 14 +++++++------- 8 files changed, 35 insertions(+), 64 deletions(-) delete mode 100755 scripts/automation/trex_control_plane/doc/authors.rst delete mode 100755 scripts/automation/trex_control_plane/doc/license.rst diff --git a/scripts/automation/trex_control_plane/doc/about_trex.rst b/scripts/automation/trex_control_plane/doc/about_trex.rst index 97cad97d..669e2b28 100755 --- a/scripts/automation/trex_control_plane/doc/about_trex.rst +++ b/scripts/automation/trex_control_plane/doc/about_trex.rst @@ -1,16 +1,19 @@ -=================== -About T-Rex project -=================== +================== +About TRex project +================== Full project's official site ---------------------------- -To learn all about T-Rex project, visit Cisco's internal `official site `_ +To learn all about TRex project, visit TRex `official site `_ -Even more ---------- +GitHub Repository +----------------- -.. toctree:: - :maxdepth: 2 +`TRex GitHub repository `_ + +TRex Google Group +----------------- + +Check out the project's `google group `_ to contact TRex DEV team and follow other user stories. - authors \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/authors.rst b/scripts/automation/trex_control_plane/doc/authors.rst deleted file mode 100755 index 3b85f020..00000000 --- a/scripts/automation/trex_control_plane/doc/authors.rst +++ /dev/null @@ -1,12 +0,0 @@ -======= -Authors -======= - -T-Rex is developed in Cisco Systems Inc. as the next generation traffic generator. - -T-Rex core-team developers are: - - - Hanoch Haim - - Dave Johnson - - Wenxian Li - - Dan Klein \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/conf.py b/scripts/automation/trex_control_plane/doc/conf.py index fb9ea83c..46d0435d 100755 --- a/scripts/automation/trex_control_plane/doc/conf.py +++ b/scripts/automation/trex_control_plane/doc/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# T-Rex Control Plain documentation build configuration file, created by +# TRex Control Plain documentation build configuration file, created by # sphinx-quickstart on Tue Jun 2 07:48:10 2015. # # This file is execfile()d with the current directory set to its @@ -54,7 +54,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'T-Rex Control Plain' +project = u'TRex Control Plain' copyright = u'2015, Cisco Systems Inc.' author = u'Dan Klein for Cisco Systems Inc.' @@ -211,7 +211,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'T-RexControlPlaindoc' +htmlhelp_basename = 'TRexControlPlaindoc' # -- Options for LaTeX output --------------------------------------------- @@ -233,7 +233,7 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'T-RexControlPlain.tex', u'T-Rex Control Plain Documentation', + (master_doc, 'TRexControlPlain.tex', u'TRex Control Plain Documentation', u'Dan Klein for Cisco Systems Inc', 'manual'), ] @@ -263,7 +263,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 't-rexcontrolplain', u'T-Rex Control Plain Documentation', + (master_doc, 'TRexcontrolplain', u'TRex Control Plain Documentation', [author], 1) ] @@ -277,8 +277,8 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'T-RexControlPlain', u'T-Rex Control Plain Documentation', - author, 'T-RexControlPlain', 'One line description of project.', + (master_doc, 'TRexControlPlain', u'TRex Control Plain Documentation', + author, 'TRexControlPlain', 'One line description of project.', 'Miscellaneous'), ] diff --git a/scripts/automation/trex_control_plane/doc/index.rst b/scripts/automation/trex_control_plane/doc/index.rst index ebcd5dcf..10803cb0 100755 --- a/scripts/automation/trex_control_plane/doc/index.rst +++ b/scripts/automation/trex_control_plane/doc/index.rst @@ -51,9 +51,7 @@ About TRex .. toctree:: :maxdepth: 2 - All about T-Rex - license - + All about TRex Indices and tables diff --git a/scripts/automation/trex_control_plane/doc/installation.rst b/scripts/automation/trex_control_plane/doc/installation.rst index dda32f56..f234c260 100755 --- a/scripts/automation/trex_control_plane/doc/installation.rst +++ b/scripts/automation/trex_control_plane/doc/installation.rst @@ -4,9 +4,9 @@ Installation Prerequisites ------------- -The T-Rex control plane is based on client-server model that interacts using JSON-RPC. +The TRex control plane is based on client-server model that interacts using JSON-RPC. -In order to use the client-side API documented a T-Rex server daemon must be up and listening on the same host and port that the client tries to connect with. +In order to use the client-side API documented a TRex server daemon must be up and listening on the same host and port that the client tries to connect with. Compatibility ------------- @@ -14,12 +14,12 @@ Both client and server side were developed for Linux platform. The client-side module is also compatible with windows python. The client side can be used with both Python 2 and Python 3 versions. -However, the server side was desined to and best fits with Python 2.7.6 and on (all 2.x series, assuming > 2.6.9). +However, the server side was designed to and best fits with Python 2.7.6 and on (all 2.x series, assuming > 2.6.9). Installation manual ------------------- -T-Rex Control Plane is a cross-platform, cross-operatin system APi to control and run T-Rex. +TRex Control Plane is a cross-platform, cross-operation system API to control and run TRex. -The full, most updated manual (which refers to all programming languages) can be found under the `Automation API documentation `_ . \ No newline at end of file +The full, most updated manual (which refers to all programming languages) can be found under the `trex-core Wiki `_ of the TRex website. \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/json_dictionary.yaml b/scripts/automation/trex_control_plane/doc/json_dictionary.yaml index 853ded65..89535b56 100755 --- a/scripts/automation/trex_control_plane/doc/json_dictionary.yaml +++ b/scripts/automation/trex_control_plane/doc/json_dictionary.yaml @@ -1,6 +1,6 @@ -################################################################ -#### T-Rex JSON Dictionary definitions #### -################################################################ +############################################################### +#### TRex JSON Dictionary definitions #### +############################################################### trex-global : diff --git a/scripts/automation/trex_control_plane/doc/license.rst b/scripts/automation/trex_control_plane/doc/license.rst deleted file mode 100755 index b83dd4b3..00000000 --- a/scripts/automation/trex_control_plane/doc/license.rst +++ /dev/null @@ -1,18 +0,0 @@ -======= -License -======= - - -Copyright 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. diff --git a/scripts/automation/trex_control_plane/doc/usage_examples.rst b/scripts/automation/trex_control_plane/doc/usage_examples.rst index 7116f28c..ff5c026d 100755 --- a/scripts/automation/trex_control_plane/doc/usage_examples.rst +++ b/scripts/automation/trex_control_plane/doc/usage_examples.rst @@ -6,7 +6,7 @@ Usage Examples Full-featured interactive shell ------------------------------- -The `client_interactive_example.py` extends and uses the `Cmd `_ built in python class to create a Full-featured shell using which one can interact with T-Rex server and get instant results. +The `client_interactive_example.py` extends and uses the `Cmd `_ built in python class to create a Full-featured shell using which one can interact with TRex server and get instant results. The help menu of this application is: @@ -14,21 +14,21 @@ The help menu of this application is: usage: client_interactive_example [options] - Run T-Rex client API demos and scenarios. + Run TRex client API demos and scenarios. optional arguments: -h, --help show this help message and exit -v, --version show program's version number and exit -t HOST, --trex-host HOST - Specify the hostname or ip to connect with T-Rex + Specify the hostname or ip to connect with TRex server. -p PORT, --trex-port PORT - Select port on which the T-Rex server listens. Default + Select port on which the TRex server listens. Default port is 8090. -m SIZE, --maxhist SIZE Specify maximum history size saved at client side. Default size is 100. - --verbose Switch ON verbose option at T-Rex client. Default is: + --verbose Switch ON verbose option at TRex client. Default is: OFF. **Code Excerpt** @@ -51,8 +51,8 @@ The demo takes the user a full circle: 2. exporting the generated packets into .pcap file named `dns_traffic.pcap`. 3. Use the :class:`trex_yaml_gen.CTRexYaml` generator to include that pcap file in the yaml object. 4. Export the YAML object onto a YAML file named `dns_traffic.yaml` - 5. Push the generated files to T-Rex server. - 6. Run T-Rex based on the generated (and pushed) files. + 5. Push the generated files to TRex server. + 6. Run TRex based on the generated (and pushed) files. **Code Excerpt** [#f1]_ -- cgit 1.2.3-korg From 2b5c0e9fc7482584d2259a7f79496ea86bcf4b5a Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 6 Oct 2015 14:43:39 +0300 Subject: progress in stateless client, added trex_stats --- .../client/trex_stateless_client.py | 90 ++++++--- .../client_utils/jsonrpc_client.py | 209 +-------------------- .../trex_control_plane/common/trex_stats.py | 45 +++++ 3 files changed, 116 insertions(+), 228 deletions(-) create mode 100755 scripts/automation/trex_control_plane/common/trex_stats.py diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index b25d5cd5..9e49b852 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -9,6 +9,7 @@ except ImportError: from client_utils.jsonrpc_client import JsonRpcClient from client_utils.packet_builder import CTRexPktBuilder import json +from common.trex_stats import * class CTRexStatelessClient(object): @@ -17,31 +18,62 @@ class CTRexStatelessClient(object): super(CTRexStatelessClient, self).__init__() self.tx_link = CTRexStatelessClient.CTxLink(server, port, virtual) self._conn_handler = {} - - def owned(func): - def wrapper(self, *args, **kwargs): - if self._conn_handler.get(kwargs.get("port_id")): - return func(self, *args, **kwargs) - else: - raise RuntimeError("The requested method ('{0}') cannot be invoked unless the desired port is owned". - format(func.__name__)) + self._active_ports = set() + self._port_stats = CTRexStatsManager() + self._stream_stats = CTRexStatsManager() + + + # ----- decorator methods ----- # + def force_status(owned=True, active=False): + def wrapper(func): + def wrapper_f(self, *args, **kwargs): + port_ids = kwargs.get("port_id") + if isinstance(port_ids, int): + # make sure port_ids is a list + port_ids = [port_ids] + bad_ids = set() + for port_id in port_ids: + if not self._conn_handler.get(kwargs.get(port_id)): + bad_ids.add(port_ids) + if bad_ids: + # Some port IDs are not according to desires status + own_str = "owned" if owned else "not-owned" + act_str = "active" if active else "non-active" + raise RuntimeError("The requested method ('{0}') cannot be invoked since port IDs {1} are not both" \ + "{2} and {3}".format(func.__name__, + bad_ids, + own_str, + act_str)) + else: + func(self, *args, **kwargs) + return wrapper_f return wrapper + # def owned(func): + # def wrapper(self, *args, **kwargs): + # if self._conn_handler.get(kwargs.get("port_id")): + # return func(self, *args, **kwargs) + # else: + # raise RuntimeError("The requested method ('{0}') cannot be invoked unless the desired port is owned". + # format(func.__name__)) + # return wrapper + + # ----- user-access methods ----- # def acquire(self, port_id, username, force=False): params = {"port_id": port_id, "user": username, "force": force} self._conn_handler[port_id] = self.transmit("acquire", params) - return self._conn_handler + return self._conn_handler[port_id] - @owned + @force_status(owned=True) def release(self, port_id=None): self._conn_handler.pop(port_id) params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} return self.transmit("release", params) - @owned + @force_status(owned=True) def add_stream(self, stream_id, stream_obj, port_id=None): assert isinstance(stream_obj, CStream) params = {"handler": self._conn_handler.get(port_id), @@ -50,15 +82,15 @@ class CTRexStatelessClient(object): "stream": stream_obj.dump()} return self.transmit("add_stream", params) - @owned + @force_status(owned=True) def remove_stream(self, stream_id, port_id=None): params = {"handler": self._conn_handler.get(port_id), "port_id": port_id, "stream_id": stream_id} return self.transmit("remove_stream", params) - @owned - def get_stream_list(self, port_id=None): + @force_status(owned=True,active=) + def get_stream_id_list(self, port_id=None): params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} return self.transmit("get_stream_list", params) @@ -86,21 +118,35 @@ class CTRexStatelessClient(object): return self.transmit("get_global_stats") @owned - def stop_traffic(self, port_id=None): - params = {"handler": self._conn_handler.get(port_id), + def get_port_stats(self, port_id=None): + params = {"handler": self._conn_handler.get(port_id), # TODO: verify if needed "port_id": port_id} - return self.transmit("stop_traffic", params) - - - + return self.transmit("get_port_stats", params) + @owned + def get_stream_stats(self, port_id=None): + params = {"handler": self._conn_handler.get(port_id), # TODO: verify if needed + "port_id": port_id} + return self.transmit("get_stream_stats", params) + # ----- internal methods ----- # + def transmit(self, method_name, params={}): + return self.tx_link.transmit(method_name, params) + @staticmethod + def _object_decoder(obj_type, obj_data): + if obj_type=="global": + return CGlobalStats(**obj_data) + elif obj_type=="port": + return CPortStats(**obj_data) + elif obj_type=="stream": + return CStreamStats(**obj_data) + else: + # Do not serialize the data into class + return obj_data - def transmit(self, method_name, params={}): - return self.tx_link.transmit(method_name, params) # ------ private classes ------ # 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 51bb3a14..a5adc485 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -60,6 +60,7 @@ class JsonRpcClient(object): return rc + # pretty print for JSON def pretty_json (self, json_str, use_colors = True): pretty_str = json.dumps(json.loads(json_str), indent = 4, separators=(',', ': '), sort_keys = True) @@ -87,6 +88,7 @@ class JsonRpcClient(object): print "[verbose] " + msg + # batch messages def create_batch (self): return BatchMessage(self) @@ -114,6 +116,7 @@ class JsonRpcClient(object): return self.send_raw_msg(msg, block) + # low level send of string message def send_raw_msg (self, msg, block = False): self.verbose_msg("Sending Request To Server:\n\n" + self.pretty_json(msg) + "\n") @@ -241,209 +244,3 @@ 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): - return self.system_info - - def get_supported_cmds(self): - return self.supported_cmds - - def get_port_count (self): - if not self.system_info: - return 0 - - return self.system_info["port_count"] - - # refresh the client for transient data - def refresh (self): - - # get server versionrc, msg = self.get_supported_cmds() - 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.refresh() - - - # 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 diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py new file mode 100755 index 00000000..62c3a890 --- /dev/null +++ b/scripts/automation/trex_control_plane/common/trex_stats.py @@ -0,0 +1,45 @@ +#!/router/bin/python +import copy + +class CTRexStatsManager(object): + + def __init__(self): + self._stats = {} + pass + + def update(self, obj_id, stats_obj): + assert isinstance(stats_obj, CTRexStats) + self._stats[obj_id] = stats_obj + + def get_stats(self, obj_id): + return copy.copy(self._stats.pop(obj_id)) + + + + +class CTRexStats(object): + def __init__(self, **kwargs): + for k, v in kwargs.items(): + setattr(self, k, v) + + +class CGlobalStats(CTRexStats): + def __init__(self, **kwargs): + super(CGlobalStats, self).__init__(kwargs) + pass + + +class CPortStats(CTRexStats): + def __init__(self, **kwargs): + super(CPortStats, self).__init__(kwargs) + pass + + +class CStreamStats(CTRexStats): + def __init__(self, **kwargs): + super(CStreamStats, self).__init__(kwargs) + pass + + +if __name__ == "__main__": + pass -- cgit 1.2.3-korg From c27d9bf5bc25a1a9e063ca076ce2e99c02dfe31e Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Wed, 7 Oct 2015 00:32:37 +0300 Subject: Incorporating batch commands in stateless client --- .../client/trex_stateless_client.py | 48 ++++++++++++++++++---- .../client_utils/jsonrpc_client.py | 2 +- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 9e49b852..3a48c612 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -6,14 +6,17 @@ try: except ImportError: # support import for Python 3 import client.outer_packages -from client_utils.jsonrpc_client import JsonRpcClient +from client_utils.jsonrpc_client import JsonRpcClient, BatchMessage from client_utils.packet_builder import CTRexPktBuilder import json from common.trex_stats import * +from collections import namedtuple class CTRexStatelessClient(object): """docstring for CTRexStatelessClient""" + RpcCmdData = namedtuple('RpcCmdData', ['method', 'params']) + def __init__(self, server="localhost", port=5050, virtual=False): super(CTRexStatelessClient, self).__init__() self.tx_link = CTRexStatelessClient.CTxLink(server, port, virtual) @@ -60,11 +63,18 @@ class CTRexStatelessClient(object): # ----- user-access methods ----- # def acquire(self, port_id, username, force=False): - params = {"port_id": port_id, - "user": username, - "force": force} - self._conn_handler[port_id] = self.transmit("acquire", params) - return self._conn_handler[port_id] + if isinstance(port_id, list) or isinstance(port_id, set): + port_ids = set(port_id) # convert to set to avoid duplications + commands = [self.RpcCmdData("acquire", {"port_id":p_id, "user":username, "force":force}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + + else: + params = {"port_id": port_id, + "user": username, + "force": force} + self._conn_handler[port_id] = self.transmit("acquire", params) + return self._conn_handler[port_id] @force_status(owned=True) def release(self, port_id=None): @@ -133,6 +143,9 @@ class CTRexStatelessClient(object): def transmit(self, method_name, params={}): return self.tx_link.transmit(method_name, params) + def transmit_batch(self, batch_list): + return self.tx_link.transmit_batch(batch_list) + @staticmethod def _object_decoder(obj_type, obj_data): if obj_type=="global": @@ -163,15 +176,32 @@ class CTRexStatelessClient(object): def transmit(self, method_name, params={}): if self.virtual: - print "Transmitting virtually over tcp://{server}:{port}".format( - server=self.server, - port=self.port) + self._prompt_virtual_tx_msg() id, msg = self.rpc_link.create_jsonrpc_v2(method_name, params) print msg return else: return self.rpc_link.invoke_rpc_method(method_name, params) + def transmit_batch(self, batch_list): + if self.virtual: + self._prompt_virtual_tx_msg() + print [msg + for id, msg in [self.rpc_link.create_jsonrpc_v2(command.method, command.params) + for command in batch_list]] + else: + batch = self.rpc_link.create_batch() + for command in batch_list: + batch.add(command.method, command.params) + # invoke the batch + return batch.invoke() + + + def _prompt_virtual_tx_msg(self): + print "Transmitting virtually over tcp://{server}:{port}".format( + server=self.server, + port=self.port) + class CStream(object): """docstring for CStream""" DEFAULTS = {"rx_stats": CRxStats, 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 a5adc485..db5ddc51 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -23,7 +23,7 @@ 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) -- cgit 1.2.3-korg From bafc3ec4b2686cdec4ac1c33f69f7607f368d4ce Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Wed, 7 Oct 2015 12:02:13 +0300 Subject: updated stats handling implementation --- .../trex_control_plane/common/trex_stats.py | 38 +++++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py index 62c3a890..33015f01 100755 --- a/scripts/automation/trex_control_plane/common/trex_stats.py +++ b/scripts/automation/trex_control_plane/common/trex_stats.py @@ -3,18 +3,32 @@ import copy class CTRexStatsManager(object): - def __init__(self): - self._stats = {} - pass - - def update(self, obj_id, stats_obj): - assert isinstance(stats_obj, CTRexStats) - self._stats[obj_id] = stats_obj - - def get_stats(self, obj_id): - return copy.copy(self._stats.pop(obj_id)) - - + def __init__(self, *args): + for stat_type in args: + # register stat handler for each stats type + setattr(self, stat_type, CTRexStatsManager.CSingleStatsHandler()) + + def __getitem__(self, item): + stats_obj = getattr(self,item) + if stats_obj: + return stats_obj.get_stats() + else: + return None + + class CSingleStatsHandler(object): + + def __init__(self): + self._stats = {} + + def update(self, obj_id, stats_obj): + assert isinstance(stats_obj, CTRexStats) + self._stats[obj_id] = stats_obj + + def get_stats(self, obj_id=None): + if obj_id: + return copy.copy(self._stats.pop(obj_id)) + else: + return copy.copy(self._stats) class CTRexStats(object): -- cgit 1.2.3-korg From 4f286bfefa6bbb0be4cdcf1fb004c82fc334c21f Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Wed, 7 Oct 2015 13:47:18 +0300 Subject: progress in TRexStatelessClient module mainly at batching support --- .../client/trex_stateless_client.py | 202 ++++++++++++++------- .../client_utils/jsonrpc_client.py | 6 +- 2 files changed, 139 insertions(+), 69 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 3a48c612..412bdc09 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -17,17 +17,18 @@ class CTRexStatelessClient(object): """docstring for CTRexStatelessClient""" RpcCmdData = namedtuple('RpcCmdData', ['method', 'params']) - def __init__(self, server="localhost", port=5050, virtual=False): + def __init__(self, username, server="localhost", port=5050, virtual=False): super(CTRexStatelessClient, self).__init__() + self.user = username self.tx_link = CTRexStatelessClient.CTxLink(server, port, virtual) self._conn_handler = {} self._active_ports = set() self._port_stats = CTRexStatsManager() self._stream_stats = CTRexStatsManager() - + self._system_info = None # ----- decorator methods ----- # - def force_status(owned=True, active=False): + def force_status(owned=True, active_and_owned=False): def wrapper(func): def wrapper_f(self, *args, **kwargs): port_ids = kwargs.get("port_id") @@ -36,52 +37,79 @@ class CTRexStatelessClient(object): port_ids = [port_ids] bad_ids = set() for port_id in port_ids: - if not self._conn_handler.get(kwargs.get(port_id)): + port_owned = self._conn_handler.get(kwargs.get(port_id)) + if owned and not port_owned: bad_ids.add(port_ids) + elif active_and_owned: # stronger condition than just owned, hence gets precedence + if port_owned and port_id in self._active_ports: + continue + else: + bad_ids.add(port_ids) + else: + continue if bad_ids: # Some port IDs are not according to desires status - own_str = "owned" if owned else "not-owned" - act_str = "active" if active else "non-active" - raise RuntimeError("The requested method ('{0}') cannot be invoked since port IDs {1} are not both" \ - "{2} and {3}".format(func.__name__, - bad_ids, - own_str, - act_str)) + raise RuntimeError("The requested method ('{0}') cannot be invoked since port IDs {1} are not" \ + "at allowed stated".format(func.__name__)) else: func(self, *args, **kwargs) return wrapper_f return wrapper - # def owned(func): - # def wrapper(self, *args, **kwargs): - # if self._conn_handler.get(kwargs.get("port_id")): - # return func(self, *args, **kwargs) - # else: - # raise RuntimeError("The requested method ('{0}') cannot be invoked unless the desired port is owned". - # format(func.__name__)) - # return wrapper + @property + def system_info(self): + if not self._system_info: + self._system_info = self.get_system_info() + return self._system_info # ----- user-access methods ----- # - def acquire(self, port_id, username, force=False): + def ping(self): + return self.transmit("ping") + + def get_supported_cmds(self): + return self.transmit("get_supported_cmds") + + def get_version(self): + return self.transmit("get_version") + + def get_system_info(self): + return self.transmit("get_system_info") + + def get_port_count(self): + return self.system_info.get("port_count") + + def acquire(self, port_id, force=False): + if not CTRexStatelessClient._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications - commands = [self.RpcCmdData("acquire", {"port_id":p_id, "user":username, "force":force}) + commands = [self.RpcCmdData("acquire", {"port_id": p_id, "user": self.user, "force": force}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) + # TODO: further processing here else: params = {"port_id": port_id, - "user": username, + "user": self.user, "force": force} self._conn_handler[port_id] = self.transmit("acquire", params) return self._conn_handler[port_id] @force_status(owned=True) def release(self, port_id=None): - self._conn_handler.pop(port_id) - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id} - return self.transmit("release", params) + if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [self.RpcCmdData("release", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + # TODO: further processing here + else: + self._conn_handler.pop(port_id) + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id} + return self.transmit("release", params) @force_status(owned=True) def add_stream(self, stream_id, stream_obj, port_id=None): @@ -99,45 +127,77 @@ class CTRexStatelessClient(object): "stream_id": stream_id} return self.transmit("remove_stream", params) - @force_status(owned=True,active=) + @force_status(owned=True, active_and_owned=True) def get_stream_id_list(self, port_id=None): params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} return self.transmit("get_stream_list", params) - @owned + @force_status(owned=True, active_and_owned=True) def get_stream(self, stream_id, port_id=None): params = {"handler": self._conn_handler.get(port_id), "port_id": port_id, "stream_id": stream_id} return self.transmit("get_stream_list", params) - @owned + @force_status(owned=True) def start_traffic(self, port_id=None): - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id} - return self.transmit("start_traffic", params) + if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [self.RpcCmdData("start_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + # TODO: further processing here + else: + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id} + return self.transmit("start_traffic", params) - @owned + @force_status(owned=False, active_and_owned=True) def stop_traffic(self, port_id=None): - params = {"handler": self._conn_handler.get(port_id), - "port_id": port_id} - return self.transmit("stop_traffic", params) + if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [self.RpcCmdData("stop_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + # TODO: further processing here + else: + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id} + return self.transmit("stop_traffic", params) def get_global_stats(self): return self.transmit("get_global_stats") - @owned + @force_status(owned=True, active_and_owned=True) def get_port_stats(self, port_id=None): - params = {"handler": self._conn_handler.get(port_id), # TODO: verify if needed - "port_id": port_id} - return self.transmit("get_port_stats", params) + if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [self.RpcCmdData("get_port_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + # TODO: further processing here + else: + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id} + return self.transmit("get_port_stats", params) - @owned + @force_status(owned=True, active_and_owned=True) def get_stream_stats(self, port_id=None): - params = {"handler": self._conn_handler.get(port_id), # TODO: verify if needed - "port_id": port_id} - return self.transmit("get_stream_stats", params) + if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [self.RpcCmdData("get_stream_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + # TODO: further processing here + else: + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id} + return self.transmit("get_stream_stats", params) # ----- internal methods ----- # def transmit(self, method_name, params={}): @@ -148,18 +208,25 @@ class CTRexStatelessClient(object): @staticmethod def _object_decoder(obj_type, obj_data): - if obj_type=="global": + if obj_type == "global": return CGlobalStats(**obj_data) - elif obj_type=="port": + elif obj_type == "port": return CPortStats(**obj_data) - elif obj_type=="stream": + elif obj_type == "stream": return CStreamStats(**obj_data) else: # Do not serialize the data into class return obj_data - - + def _is_ports_valid(self, port_id): + if isinstance(port_id, list) or isinstance(port_id, set): + # check each item of the sequence + return all([CTRexStatelessClient._is_ports_valid(port) + for port in port_id]) + elif (isinstance(port_id, int)) and (port_id > 0) and (port_id <= self.get_port_count()): + return True + else: + return False # ------ private classes ------ # @@ -196,12 +263,12 @@ class CTRexStatelessClient(object): # invoke the batch return batch.invoke() - def _prompt_virtual_tx_msg(self): print "Transmitting virtually over tcp://{server}:{port}".format( server=self.server, port=self.port) + class CStream(object): """docstring for CStream""" DEFAULTS = {"rx_stats": CRxStats, @@ -217,7 +284,9 @@ class CStream(object): setattr(self, k, v) # set default values to unset attributes, according to DEFAULTS dict set_keys = set(kwargs.keys()) - keys_to_set = [x for x in self.DEFAULTS if x not in set_keys] + keys_to_set = [x + for x in self.DEFAULTS + if x not in set_keys] for key in keys_to_set: default = self.DEFAULTS.get(key) if type(default)==type: @@ -260,21 +329,21 @@ class CStream(object): def dump(self): pass - return {"enabled":self.enabled, - "self_start":self.self_start, - "isg":self.isg, - "next_stream":self.next_stream, - "packet":self.packet.dump_pkt(), - "mode":self.mode.dump(), - "vm":self.packet.get_vm_data(), - "rx_stats":self.rx_stats.dump()} + return {"enabled": self.enabled, + "self_start": self.self_start, + "isg": self.isg, + "next_stream": self.next_stream, + "packet": self.packet.dump_pkt(), + "mode": self.mode.dump(), + "vm": self.packet.get_vm_data(), + "rx_stats": self.rx_stats.dump()} class CRxStats(object): def __init__(self, enabled=False, seq_enabled=False, latency_enabled=False): - self._rx_dict = {"enabled" : enabled, - "seq_enabled" : seq_enabled, - "latency_enabled" : latency_enabled} + self._rx_dict = {"enabled": enabled, + "seq_enabled": seq_enabled, + "latency_enabled": latency_enabled} @property def enabled(self): @@ -301,11 +370,12 @@ class CRxStats(object): self._rx_dict['latency_enabled'] = bool(bool_value) def dump(self): - return {k:v + return {k: v for k,v in self._rx_dict.items() if v } + class CTxMode(object): """docstring for CTxMode""" def __init__(self, tx_mode, pps): @@ -313,7 +383,7 @@ class CTxMode(object): if tx_mode not in ["continuous", "single_burst", "multi_burst"]: raise ValueError("Unknown TX mode ('{0}')has been initialized.".format(tx_mode)) self._tx_mode = tx_mode - self._fields = {'pps':float(pps)} + self._fields = {'pps': float(pps)} if tx_mode == "single_burst": self._fields['total_pkts'] = 0 elif tx_mode == "multi_burst": @@ -331,8 +401,8 @@ class CTxMode(object): format(attr, self._tx_mode)) def dump(self): - dump = {"type":self._tx_mode} - dump.update({k:v + dump = {"type": self._tx_mode} + dump.update({k: v for k, v in self._fields.items() }) return dump 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 db5ddc51..b8b1734d 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -171,7 +171,7 @@ class JsonRpcClient(object): def process_single_response (self, response_json): if (response_json.get("jsonrpc") != "2.0"): - return False, "Malfromed Response ({0})".format(str(response)) + return False, "Malformed Response ({0})".format(str(response)) # error reported by server if ("error" in response_json): @@ -182,7 +182,7 @@ class JsonRpcClient(object): # if no error there should be a result if ("result" not in response_json): - return False, "Malfromed Response ({0})".format(str(response)) + return False, "Malformed Response ({0})".format(str(response)) return True, response_json["result"] @@ -191,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) -- cgit 1.2.3-korg From c3ced9cd49c609d8a25933012f9aa2e5db9298d9 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Wed, 7 Oct 2015 13:49:28 +0300 Subject: Applied port validity on port_id relevant methods --- .../trex_control_plane/client/trex_stateless_client.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 412bdc09..95ed8522 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -98,6 +98,8 @@ class CTRexStatelessClient(object): @force_status(owned=True) def release(self, port_id=None): + if not CTRexStatelessClient._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications @@ -113,6 +115,8 @@ class CTRexStatelessClient(object): @force_status(owned=True) def add_stream(self, stream_id, stream_obj, port_id=None): + if not CTRexStatelessClient._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") assert isinstance(stream_obj, CStream) params = {"handler": self._conn_handler.get(port_id), "port_id": port_id, @@ -122,6 +126,8 @@ class CTRexStatelessClient(object): @force_status(owned=True) def remove_stream(self, stream_id, port_id=None): + if not CTRexStatelessClient._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") params = {"handler": self._conn_handler.get(port_id), "port_id": port_id, "stream_id": stream_id} @@ -129,12 +135,16 @@ class CTRexStatelessClient(object): @force_status(owned=True, active_and_owned=True) def get_stream_id_list(self, port_id=None): + if not CTRexStatelessClient._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} return self.transmit("get_stream_list", params) @force_status(owned=True, active_and_owned=True) def get_stream(self, stream_id, port_id=None): + if not CTRexStatelessClient._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") params = {"handler": self._conn_handler.get(port_id), "port_id": port_id, "stream_id": stream_id} @@ -142,6 +152,8 @@ class CTRexStatelessClient(object): @force_status(owned=True) def start_traffic(self, port_id=None): + if not CTRexStatelessClient._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications @@ -156,6 +168,8 @@ class CTRexStatelessClient(object): @force_status(owned=False, active_and_owned=True) def stop_traffic(self, port_id=None): + if not CTRexStatelessClient._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications @@ -173,6 +187,8 @@ class CTRexStatelessClient(object): @force_status(owned=True, active_and_owned=True) def get_port_stats(self, port_id=None): + if not CTRexStatelessClient._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications @@ -187,6 +203,8 @@ class CTRexStatelessClient(object): @force_status(owned=True, active_and_owned=True) def get_stream_stats(self, port_id=None): + if not CTRexStatelessClient._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode port_ids = set(port_id) # convert to set to avoid duplications -- cgit 1.2.3-korg From 086dac9854b9711cebf73d392973cae9358b6b0e Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Wed, 7 Oct 2015 23:43:18 +0300 Subject: More progress in stateless client. Mainly more mature approach to handling results --- .../client/trex_stateless_client.py | 68 ++++++++++++++++++---- .../client_utils/jsonrpc_client.py | 2 +- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 95ed8522..faccf168 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -23,8 +23,7 @@ class CTRexStatelessClient(object): self.tx_link = CTRexStatelessClient.CTxLink(server, port, virtual) self._conn_handler = {} self._active_ports = set() - self._port_stats = CTRexStatsManager() - self._stream_stats = CTRexStatsManager() + self._stats = CTRexStatsManager("port", "stream") self._system_info = None # ----- decorator methods ----- # @@ -87,14 +86,15 @@ class CTRexStatelessClient(object): commands = [self.RpcCmdData("acquire", {"port_id": p_id, "user": self.user, "force": force}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) - # TODO: further processing here - + if rc: + self._process_batch_result(commands, resp_list, self._handle_acquire_response) else: params = {"port_id": port_id, "user": self.user, "force": force} - self._conn_handler[port_id] = self.transmit("acquire", params) - return self._conn_handler[port_id] + command = self.RpcCmdData("acquire", params) + self._handle_acquire_response(command, self.transmit(command.method, command.params)) + return self._conn_handler.get(port_id) @force_status(owned=True) def release(self, port_id=None): @@ -106,12 +106,15 @@ class CTRexStatelessClient(object): commands = [self.RpcCmdData("release", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) - # TODO: further processing here + if rc: + self._process_batch_result(commands, resp_list, self._handle_release_response) else: self._conn_handler.pop(port_id) params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} - return self.transmit("release", params) + command = self.RpcCmdData("release", params) + self._handle_release_response(command, self.transmit(command.method, command.params)) + return @force_status(owned=True) def add_stream(self, stream_id, stream_obj, port_id=None): @@ -160,11 +163,14 @@ class CTRexStatelessClient(object): commands = [self.RpcCmdData("start_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) - # TODO: further processing here + if rc: + self._process_batch_result(commands, resp_list, self._handle_start_traffic_response) else: params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} - return self.transmit("start_traffic", params) + command = self.RpcCmdData("start_traffic", params) + self._handle_start_traffic_response(command, self.transmit(command.method, command.params)) + return @force_status(owned=False, active_and_owned=True) def stop_traffic(self, port_id=None): @@ -176,11 +182,14 @@ class CTRexStatelessClient(object): commands = [self.RpcCmdData("stop_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) - # TODO: further processing here + if rc: + self._process_batch_result(commands, resp_list, self._handle_stop_traffic_response) else: params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} - return self.transmit("stop_traffic", params) + command = self.RpcCmdData("stop_traffic", params) + self._handle_start_traffic_response(command, self.transmit(command.method, command.params)) + return def get_global_stats(self): return self.transmit("get_global_stats") @@ -236,6 +245,31 @@ class CTRexStatelessClient(object): # Do not serialize the data into class return obj_data + @staticmethod + def default_success_test(result_obj): + if result_obj[0]: + return True + else: + return False + + # ----- handler internal methods ----- # + def _handle_acquire_response(self, request, response): + if response[0]: + self._conn_handler[request.get("port_id")] = response[1] + + def _handle_release_response(self, request, response): + if response[0]: + del self._conn_handler[request.get("port_id")] + + def _handle_start_traffic_response(self, request, response): + if response[0]: + self._active_ports.add(request.get("port_id")) + + def _handle_stop_traffic_response(self, request, response): + if response[0]: + self._active_ports.remove(request.get("port_id")) + + def _is_ports_valid(self, port_id): if isinstance(port_id, list) or isinstance(port_id, set): # check each item of the sequence @@ -246,6 +280,16 @@ class CTRexStatelessClient(object): else: return False + def _process_batch_result(self, req_list, resp_list, handler_func=None, success_test=default_success_test): + for i, response in enumerate(resp_list): + if success_test(): + # run handler method with its params + handler_func(req_list[i], response) + else: + continue # TODO: mark in this case somehow the bad result + + + # ------ private classes ------ # class CTxLink(object): 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 b8b1734d..78375874 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -28,7 +28,7 @@ class BatchMessage(object): 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" -- cgit 1.2.3-korg From d1065266e17e514dab4aec87abab729a518cdf26 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Mon, 12 Oct 2015 07:12:24 +0300 Subject: Applied some code cosmetics --- .../client/trex_stateless_client.py | 32 ++++++++++------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index faccf168..7c373e42 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -82,7 +82,7 @@ class CTRexStatelessClient(object): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode - port_ids = set(port_id) # convert to set to avoid duplications + port_ids = set(port_id) # convert to set to avoid duplications commands = [self.RpcCmdData("acquire", {"port_id": p_id, "user": self.user, "force": force}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) @@ -159,7 +159,7 @@ class CTRexStatelessClient(object): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode - port_ids = set(port_id) # convert to set to avoid duplications + port_ids = set(port_id) # convert to set to avoid duplications commands = [self.RpcCmdData("start_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) @@ -178,7 +178,7 @@ class CTRexStatelessClient(object): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode - port_ids = set(port_id) # convert to set to avoid duplications + port_ids = set(port_id) # convert to set to avoid duplications commands = [self.RpcCmdData("stop_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) @@ -200,7 +200,7 @@ class CTRexStatelessClient(object): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode - port_ids = set(port_id) # convert to set to avoid duplications + port_ids = set(port_id) # convert to set to avoid duplications commands = [self.RpcCmdData("get_port_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) @@ -216,7 +216,7 @@ class CTRexStatelessClient(object): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode - port_ids = set(port_id) # convert to set to avoid duplications + port_ids = set(port_id) # convert to set to avoid duplications commands = [self.RpcCmdData("get_stream_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) @@ -263,12 +263,11 @@ class CTRexStatelessClient(object): def _handle_start_traffic_response(self, request, response): if response[0]: - self._active_ports.add(request.get("port_id")) + self._active_ports.add(request.get("port_id")) def _handle_stop_traffic_response(self, request, response): if response[0]: - self._active_ports.remove(request.get("port_id")) - + self._active_ports.remove(request.get("port_id")) def _is_ports_valid(self, port_id): if isinstance(port_id, list) or isinstance(port_id, set): @@ -289,8 +288,6 @@ class CTRexStatelessClient(object): continue # TODO: mark in this case somehow the bad result - - # ------ private classes ------ # class CTxLink(object): """describes the connectivity of the stateless client method""" @@ -316,8 +313,8 @@ class CTRexStatelessClient(object): if self.virtual: self._prompt_virtual_tx_msg() print [msg - for id, msg in [self.rpc_link.create_jsonrpc_v2(command.method, command.params) - for command in batch_list]] + for _, msg in [self.rpc_link.create_jsonrpc_v2(command.method, command.params) + for command in batch_list]] else: batch = self.rpc_link.create_batch() for command in batch_list: @@ -326,9 +323,8 @@ class CTRexStatelessClient(object): return batch.invoke() def _prompt_virtual_tx_msg(self): - print "Transmitting virtually over tcp://{server}:{port}".format( - server=self.server, - port=self.port) + print "Transmitting virtually over tcp://{server}:{port}".format(server=self.server, + port=self.port) class CStream(object): @@ -351,7 +347,7 @@ class CStream(object): if x not in set_keys] for key in keys_to_set: default = self.DEFAULTS.get(key) - if type(default)==type: + if type(default) == type: setattr(self, key, default()) else: setattr(self, key, default) @@ -433,7 +429,7 @@ class CRxStats(object): def dump(self): return {k: v - for k,v in self._rx_dict.items() + for k, v in self._rx_dict.items() if v } @@ -460,7 +456,7 @@ class CTxMode(object): self._fields[attr] = type(self._fields.get(attr))(val) else: raise ValueError("The provided attribute ('{0}') is not a legal attribute in selected TX mode ('{1}')". - format(attr, self._tx_mode)) + format(attr, self._tx_mode)) def dump(self): dump = {"type": self._tx_mode} -- cgit 1.2.3-korg From a9f60d36e81c25244dad8f4f4c985f1e8e368c7c Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Mon, 12 Oct 2015 00:27:49 +0300 Subject: Updated handlers of getter methods and stats (Global, port, stream). Also, set return values of RPC commands as namedtuples --- .../client/trex_stateless_client.py | 75 ++++++++++++++-------- .../client_utils/jsonrpc_client.py | 13 ++-- .../trex_control_plane/common/trex_stats.py | 3 +- 3 files changed, 60 insertions(+), 31 deletions(-) diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 7c373e42..334496d1 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -48,7 +48,7 @@ class CTRexStatelessClient(object): continue if bad_ids: # Some port IDs are not according to desires status - raise RuntimeError("The requested method ('{0}') cannot be invoked since port IDs {1} are not" \ + raise RuntimeError("The requested method ('{0}') cannot be invoked since port IDs {1} are not" "at allowed stated".format(func.__name__)) else: func(self, *args, **kwargs) @@ -78,7 +78,7 @@ class CTRexStatelessClient(object): return self.system_info.get("port_count") def acquire(self, port_id, force=False): - if not CTRexStatelessClient._is_ports_valid(port_id): + if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode @@ -98,7 +98,7 @@ class CTRexStatelessClient(object): @force_status(owned=True) def release(self, port_id=None): - if not CTRexStatelessClient._is_ports_valid(port_id): + if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode @@ -118,7 +118,7 @@ class CTRexStatelessClient(object): @force_status(owned=True) def add_stream(self, stream_id, stream_obj, port_id=None): - if not CTRexStatelessClient._is_ports_valid(port_id): + if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") assert isinstance(stream_obj, CStream) params = {"handler": self._conn_handler.get(port_id), @@ -129,7 +129,7 @@ class CTRexStatelessClient(object): @force_status(owned=True) def remove_stream(self, stream_id, port_id=None): - if not CTRexStatelessClient._is_ports_valid(port_id): + if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") params = {"handler": self._conn_handler.get(port_id), "port_id": port_id, @@ -138,7 +138,7 @@ class CTRexStatelessClient(object): @force_status(owned=True, active_and_owned=True) def get_stream_id_list(self, port_id=None): - if not CTRexStatelessClient._is_ports_valid(port_id): + if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} @@ -146,7 +146,7 @@ class CTRexStatelessClient(object): @force_status(owned=True, active_and_owned=True) def get_stream(self, stream_id, port_id=None): - if not CTRexStatelessClient._is_ports_valid(port_id): + if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") params = {"handler": self._conn_handler.get(port_id), "port_id": port_id, @@ -155,7 +155,7 @@ class CTRexStatelessClient(object): @force_status(owned=True) def start_traffic(self, port_id=None): - if not CTRexStatelessClient._is_ports_valid(port_id): + if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode @@ -174,7 +174,7 @@ class CTRexStatelessClient(object): @force_status(owned=False, active_and_owned=True) def stop_traffic(self, port_id=None): - if not CTRexStatelessClient._is_ports_valid(port_id): + if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode @@ -192,11 +192,13 @@ class CTRexStatelessClient(object): return def get_global_stats(self): - return self.transmit("get_global_stats") + command = self.RpcCmdData("get_global_stats", {}) + return self._handle_get_global_stats_response(command, self.transmit(command.method, command.params)) + # return self.transmit("get_global_stats") @force_status(owned=True, active_and_owned=True) def get_port_stats(self, port_id=None): - if not CTRexStatelessClient._is_ports_valid(port_id): + if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode @@ -204,15 +206,17 @@ class CTRexStatelessClient(object): commands = [self.RpcCmdData("get_port_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) - # TODO: further processing here + if rc: + self._process_batch_result(commands, resp_list, self._handle_get_port_stats_response) else: params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} - return self.transmit("get_port_stats", params) + command = self.RpcCmdData("get_port_stats", params) + return self._handle_get_port_stats_response(command, self.transmit(command.method, command.params)) @force_status(owned=True, active_and_owned=True) def get_stream_stats(self, port_id=None): - if not CTRexStatelessClient._is_ports_valid(port_id): + if not self._is_ports_valid(port_id): raise ValueError("Provided illegal port id input") if isinstance(port_id, list) or isinstance(port_id, set): # handle as batch mode @@ -220,11 +224,13 @@ class CTRexStatelessClient(object): commands = [self.RpcCmdData("get_stream_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) for p_id in port_ids] rc, resp_list = self.transmit_batch(commands) - # TODO: further processing here + if rc: + self._process_batch_result(commands, resp_list, self._handle_get_stream_stats_response) else: params = {"handler": self._conn_handler.get(port_id), "port_id": port_id} - return self.transmit("get_stream_stats", params) + command = self.RpcCmdData("get_stream_stats", params) + return self._handle_get_stream_stats_response(command, self.transmit(command.method, command.params)) # ----- internal methods ----- # def transmit(self, method_name, params={}): @@ -247,32 +253,50 @@ class CTRexStatelessClient(object): @staticmethod def default_success_test(result_obj): - if result_obj[0]: + if result_obj.success: return True else: return False # ----- handler internal methods ----- # def _handle_acquire_response(self, request, response): - if response[0]: - self._conn_handler[request.get("port_id")] = response[1] + if response.success: + self._conn_handler[request.get("port_id")] = response.data def _handle_release_response(self, request, response): - if response[0]: + if response.success: del self._conn_handler[request.get("port_id")] def _handle_start_traffic_response(self, request, response): - if response[0]: + if response.success: self._active_ports.add(request.get("port_id")) def _handle_stop_traffic_response(self, request, response): - if response[0]: + if response.success: self._active_ports.remove(request.get("port_id")) + def _handle_get_global_stats_response(self, request, response): + if response.success: + return CGlobalStats(**response.success) + else: + return False + + def _handle_get_port_stats_response(self, request, response): + if response.success: + return CPortStats(**response.success) + else: + return False + + def _handle_get_stream_stats_response(self, request, response): + if response.success: + return CStreamStats(**response.success) + else: + return False + def _is_ports_valid(self, port_id): if isinstance(port_id, list) or isinstance(port_id, set): # check each item of the sequence - return all([CTRexStatelessClient._is_ports_valid(port) + return all([self._is_ports_valid(port) for port in port_id]) elif (isinstance(port_id, int)) and (port_id > 0) and (port_id <= self.get_port_count()): return True @@ -281,7 +305,8 @@ class CTRexStatelessClient(object): def _process_batch_result(self, req_list, resp_list, handler_func=None, success_test=default_success_test): for i, response in enumerate(resp_list): - if success_test(): + # testing each result with success test so that a conclusion report could be deployed in future. + if success_test(response): # run handler method with its params handler_func(req_list[i], response) else: @@ -303,7 +328,7 @@ class CTRexStatelessClient(object): def transmit(self, method_name, params={}): if self.virtual: self._prompt_virtual_tx_msg() - id, msg = self.rpc_link.create_jsonrpc_v2(method_name, params) + _, msg = self.rpc_link.create_jsonrpc_v2(method_name, params) print msg return else: 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 78375874..02a9dc10 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -6,6 +6,9 @@ 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' @@ -127,7 +130,7 @@ class JsonRpcClient(object): self.socket.send(msg, flags = zmq.NOBLOCK) except zmq.error.ZMQError as e: self.disconnect() - return False, "Failed To Get Send Message" + return CmdResponse(False, "Failed To Get Send Message") got_response = False @@ -145,7 +148,7 @@ class JsonRpcClient(object): if not got_response: self.disconnect() - return False, "Failed To Get Server Response" + return CmdResponse(False, "Failed To Get Server Response") self.verbose_msg("Server Response:\n\n" + self.pretty_json(response) + "\n") @@ -159,13 +162,13 @@ class JsonRpcClient(object): for single_response in response_json: rc, msg = self.process_single_response(single_response) - rc_list.append( (rc, msg) ) + rc_list.append( CmdResponse(rc, msg) ) - return True, rc_list + return CmdResponse(True, rc_list) else: rc, msg = self.process_single_response(response_json) - return rc, msg + return CmdResponse(rc, msg) def process_single_response (self, response_json): diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py index 33015f01..b7e768c1 100755 --- a/scripts/automation/trex_control_plane/common/trex_stats.py +++ b/scripts/automation/trex_control_plane/common/trex_stats.py @@ -1,6 +1,7 @@ #!/router/bin/python import copy + class CTRexStatsManager(object): def __init__(self, *args): @@ -9,7 +10,7 @@ class CTRexStatsManager(object): setattr(self, stat_type, CTRexStatsManager.CSingleStatsHandler()) def __getitem__(self, item): - stats_obj = getattr(self,item) + stats_obj = getattr(self, item) if stats_obj: return stats_obj.get_stats() else: -- cgit 1.2.3-korg From e6bf849809c1ff84eb887973576611f2457774eb Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Mon, 12 Oct 2015 08:32:40 +0300 Subject: named with nametuples the returnvalue of the "send_raw_msg" method. --- scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 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 02a9dc10..163c6923 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -39,9 +39,9 @@ class BatchMessage(object): rc, resp_list = self.rpc_client.send_raw_msg(msg, block = False) if len(self.batch_list) == 1: - return True, [(rc, resp_list)] + return CmdResponse(True, [CmdResponse(rc, resp_list)]) else: - return rc, resp_list + return CmdResponse(rc, resp_list) # JSON RPC v2.0 client -- cgit 1.2.3-korg