From 0c5a4348a31e0e8d76dd1fcf378cb2c0a2867f59 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 15 Oct 2015 09:57:35 +0300 Subject: updated yaml utils and stream object --- .../trex_control_plane/common/external_packages.py | 28 +++ .../trex_control_plane/common/rpc_defaults.yaml | 107 ++++++++++++ .../trex_control_plane/common/trex_status.py | 8 + .../trex_control_plane/common/trex_streams.py | 190 +++++++++++++++++++++ 4 files changed, 333 insertions(+) create mode 100755 scripts/automation/trex_control_plane/common/external_packages.py create mode 100644 scripts/automation/trex_control_plane/common/rpc_defaults.yaml create mode 100644 scripts/automation/trex_control_plane/common/trex_status.py create mode 100644 scripts/automation/trex_control_plane/common/trex_streams.py (limited to 'scripts/automation/trex_control_plane/common') diff --git a/scripts/automation/trex_control_plane/common/external_packages.py b/scripts/automation/trex_control_plane/common/external_packages.py new file mode 100755 index 00000000..62121d4f --- /dev/null +++ b/scripts/automation/trex_control_plane/common/external_packages.py @@ -0,0 +1,28 @@ +#!/router/bin/python + +import sys +import os + +CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) +ROOT_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir)) # path to trex_control_plane directory +PATH_TO_PYTHON_LIB = os.path.abspath(os.path.join(ROOT_PATH, os.pardir, os.pardir, 'external_libs')) + +CLIENT_UTILS_MODULES = ['PyYAML-3.01/lib' + ] + +def import_common_modules(): + # must be in a higher priority + sys.path.insert(0, PATH_TO_PYTHON_LIB) + sys.path.append(ROOT_PATH) + import_module_list(CLIENT_UTILS_MODULES) + + +def import_module_list(modules_list): + assert(isinstance(modules_list, list)) + for p in modules_list: + full_path = os.path.join(PATH_TO_PYTHON_LIB, p) + fix_path = os.path.normcase(full_path) + sys.path.insert(1, full_path) + +import_common_modules() + diff --git a/scripts/automation/trex_control_plane/common/rpc_defaults.yaml b/scripts/automation/trex_control_plane/common/rpc_defaults.yaml new file mode 100644 index 00000000..fbaca40e --- /dev/null +++ b/scripts/automation/trex_control_plane/common/rpc_defaults.yaml @@ -0,0 +1,107 @@ +############################################################## +#### TRex RPC stream list default values #### +############################################################## + +# this document is based on TRex RPC server spec and its fields: +# http://trex-tgn.cisco.com/trex/doc/trex_rpc_server_spec.html + +### HOW TO READ THIS FILE +# 1. Each key represents an object type +# 2. Each value can be either a value field or another object +# 2.1. If a value field, read as: +# + type: type of field +# + has_default: if the value has any default +# + default: the default value (Only appears if has_default field is 'YES') +# 2.2. If an object type, jump to correspoding object key. +# 3. If an object has more than one instance type, another layer with the type shall be added. +# For example, 'mode' object has 3 types: 'continuous', 'single_burst', 'multi_burst' +# So, 3 mode objects will be defined, named: +# - mode['continuous'] +# - mode['single_burst'] +# - mode['multi_burst'] +# In this case, there's no default for the 'type' field on the object +# 4. Some values has 'multiplier' property attached. +# In such case, the loaded value will be multiplied by the multiplier +# For example, if the mode's 'pps' field value is 10, and its multiplier is 5, +# the loaded pps value will be 10*5=50 + + +stream: + enabled: + type: boolean + has_default: YES + default: True + self_start: + type: boolean + has_default: YES + default: True + isg: + type: double + has_default: YES + default: 0.0 + next_stream_id: + type: [int, string] # string to allow naming binding + has_default: YES + default: -1 # no next streams + packet: + type: object + mode: + type: object + vm: + type: array + has_default: YES + default: [] # no ranging instructions + rx_stats: + type: object + +packet: + binary: + type: [array,string] + has_default: NO + meta: + type: string + has_default: YES + default: "" + +mode: + continuous: + pps: + type: double + has_default: NO + multiplier: 1.0 + single_burst: + pps: + type: int + has_default: NO + total_pkts: + type: int + has_default: NO + multiplier: 1.0 + multi_burst: + pps: + type: int + has_default: NO + pkts_per_burst: + type: int + has_default: NO + ibg: + type: double + has_default: YES + default: 100.0 + count: + type: int + has_default: YES + default: 0 # loop forever + multiplier: 1.0 + +rx_stats: + enabled: + type: boolean + has_default: YES + default: False + seq_enabled: + type: boolean + has_default: NO + latency_enabled: + type: boolean + has_default: NO \ No newline at end of file diff --git a/scripts/automation/trex_control_plane/common/trex_status.py b/scripts/automation/trex_control_plane/common/trex_status.py new file mode 100644 index 00000000..f132720c --- /dev/null +++ b/scripts/automation/trex_control_plane/common/trex_status.py @@ -0,0 +1,8 @@ +#!/router/bin/python + +# define the states in which a T-Rex can hold during its lifetime +# TRexStatus = Enum('TRexStatus', 'Idle Starting Running') + +IDLE = 1 +STARTING = 2 +RUNNING = 3 diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py new file mode 100644 index 00000000..3b0e7376 --- /dev/null +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -0,0 +1,190 @@ +#!/router/bin/python + +import external_packages +from client_utils.packet_builder import CTRexPktBuilder +from collections import OrderedDict + + +class CStreamList(object): + + def __init__(self): + self.streams_list = OrderedDict() + self._stream_id = 0 + # self._stream_by_name = {} + + def append_stream(self, name, stream_obj): + assert isinstance(stream_obj, CStream) + if name in self.streams_list: + raise NameError("A stream with this name already exists on this list.") + self.streams_list[name]=stream_obj + return + + def remove_stream(self, name): + return self.streams_list.pop(name) + + def export_to_yaml(self, file_path): + pass + + def load_yaml(self, file_path): + # clear all existing streams linked to this object + self.streams_list.clear() + # self._stream_id = 0 + # load from YAML file the streams one by one + try: + with open(file_path, 'r') as f: + loaded_streams = yaml.load(f) + + # assume at this point that YAML file is according to rules and correct + + + except yaml.YAMLError as e: + print "Error in YAML configuration file:", e + print "Aborting YAML loading, no changes made to stream list" + return + + + pass + + + + +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__": + pass -- cgit From 2c38ad7f49fbddfdc75c150cbb2abdd54e3ede52 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Fri, 16 Oct 2015 15:37:46 +0300 Subject: Updated YAMLutils and rpc_defaults with relevant progress --- .../trex_control_plane/common/rpc_defaults.yaml | 28 ++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) mode change 100644 => 100755 scripts/automation/trex_control_plane/common/rpc_defaults.yaml (limited to 'scripts/automation/trex_control_plane/common') diff --git a/scripts/automation/trex_control_plane/common/rpc_defaults.yaml b/scripts/automation/trex_control_plane/common/rpc_defaults.yaml old mode 100644 new mode 100755 index fbaca40e..576710a3 --- a/scripts/automation/trex_control_plane/common/rpc_defaults.yaml +++ b/scripts/automation/trex_control_plane/common/rpc_defaults.yaml @@ -12,7 +12,7 @@ # + type: type of field # + has_default: if the value has any default # + default: the default value (Only appears if has_default field is 'YES') -# 2.2. If an object type, jump to correspoding object key. +# 2.2. If an object type, jump to corresponding object key. # 3. If an object has more than one instance type, another layer with the type shall be added. # For example, 'mode' object has 3 types: 'continuous', 'single_burst', 'multi_burst' # So, 3 mode objects will be defined, named: @@ -20,10 +20,12 @@ # - mode['single_burst'] # - mode['multi_burst'] # In this case, there's no default for the 'type' field on the object -# 4. Some values has 'multiplier' property attached. +# 4. Some values has 'multiply' property attached. # In such case, the loaded value will be multiplied by the multiplier # For example, if the mode's 'pps' field value is 10, and its multiplier is 5, # the loaded pps value will be 10*5=50 +# 5. Any object type must be listed by the user, even if all its field are defaults. +# The most basic option would be to declare the object with "[]", which stands for empty object in YAML syntax. stream: @@ -36,7 +38,7 @@ stream: has_default: YES default: True isg: - type: double + type: [int, double, string] has_default: YES default: 0.0 next_stream_id: @@ -66,33 +68,33 @@ packet: mode: continuous: pps: - type: double + type: [int, double] has_default: NO - multiplier: 1.0 + multiply: YES single_burst: pps: - type: int + type: [int, double] has_default: NO + multiply: YES total_pkts: type: int has_default: NO - multiplier: 1.0 multi_burst: pps: - type: int + type: [int, double] has_default: NO + multiply: YES pkts_per_burst: type: int has_default: NO ibg: - type: double + type: [int, double, string] has_default: YES default: 100.0 count: type: int has_default: YES default: 0 # loop forever - multiplier: 1.0 rx_stats: enabled: @@ -101,7 +103,9 @@ rx_stats: default: False seq_enabled: type: boolean - has_default: NO + has_default: YES + default: False latency_enabled: type: boolean - has_default: NO \ No newline at end of file + has_default: YES + default: False \ No newline at end of file -- cgit From 4a8d34c7548e85e97426bc1d85c670003b1f5870 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 18 Oct 2015 10:24:53 +0300 Subject: more yaml utils, better streams handling --- .../trex_control_plane/common/rpc_defaults.yaml | 2 +- .../trex_control_plane/common/trex_streams.py | 230 +++++++++++++-------- 2 files changed, 146 insertions(+), 86 deletions(-) (limited to 'scripts/automation/trex_control_plane/common') diff --git a/scripts/automation/trex_control_plane/common/rpc_defaults.yaml b/scripts/automation/trex_control_plane/common/rpc_defaults.yaml index 576710a3..5816f980 100755 --- a/scripts/automation/trex_control_plane/common/rpc_defaults.yaml +++ b/scripts/automation/trex_control_plane/common/rpc_defaults.yaml @@ -41,7 +41,7 @@ stream: type: [int, double, string] has_default: YES default: 0.0 - next_stream_id: + next_stream: type: [int, string] # string to allow naming binding has_default: YES default: -1 # no next streams diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py index 3b0e7376..e6aa66f2 100644 --- a/scripts/automation/trex_control_plane/common/trex_streams.py +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -3,12 +3,15 @@ import external_packages from client_utils.packet_builder import CTRexPktBuilder from collections import OrderedDict +from client_utils.yaml_utils import * +import dpkt class CStreamList(object): def __init__(self): self.streams_list = OrderedDict() + self.yaml_loader = CTRexYAMLLoader("rpc_exceptions.yaml") self._stream_id = 0 # self._stream_by_name = {} @@ -22,101 +25,50 @@ class CStreamList(object): def remove_stream(self, name): return self.streams_list.pop(name) + def rearrange_streams(self, streams_names_list, new_streams_dict={}): + tmp_list = OrderedDict() + for stream in streams_names_list: + if stream in self.streams_list: + tmp_list[stream] = self.streams_list.get(stream) + elif stream in new_streams_dict: + new_stream_obj = new_streams_dict.get(stream) + assert isinstance(new_stream_obj, CStream) + tmp_list[stream] = new_stream_obj + else: + raise NameError("Given stream named '{0}' cannot be found in existing stream list or and wasn't" + "provided with the new_stream_dict parameter.".format(stream)) + self.streams_list = tmp_list + def export_to_yaml(self, file_path): - pass + raise NotImplementedError("export_to_yaml method is not implemented, yet") - def load_yaml(self, file_path): + def load_yaml(self, file_path, multiplier_dict={}): # clear all existing streams linked to this object self.streams_list.clear() - # self._stream_id = 0 - # load from YAML file the streams one by one - try: - with open(file_path, 'r') as f: - loaded_streams = yaml.load(f) + streams_data = load_yaml_to_obj(file_path) + assert isinstance(streams_data, list) + raw_streams = {} + for stream in streams_data: + stream_name = stream.get("name") + raw_stream = stream.get("stream") + if not stream_name or not raw_stream: + raise ValueError("Provided stream is not according to convention." + "Each stream must be provided as two keys: 'name' and 'stream'. " + "Provided item was:\n {stream}".format(stream)) + new_stream_data = self.yaml_loader.validate_yaml(raw_stream, + "stream", + multiplier= multiplier_dict.get(stream_name, 1)) + new_stream_obj = CStream() + new_stream_obj.load_data(**new_stream_data) + self.append_stream(stream_name, new_stream_obj) - # assume at this point that YAML file is according to rules and correct - except yaml.YAMLError as e: - print "Error in YAML configuration file:", e - print "Aborting YAML loading, no changes made to stream list" - return + # start validating and reassembling clients input pass - - - -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): @@ -154,7 +106,6 @@ class CRxStats(object): if v } - class CTxMode(object): """docstring for CTxMode""" def __init__(self, tx_mode, pps): @@ -186,5 +137,114 @@ class CTxMode(object): }) return dump +class CStream(object): + """docstring for CStream""" + DEFAULTS = {"rx_stats": CRxStats, + "mode": CTxMode, + "isg": 5.0, + "next_stream": -1, + "self_start": True, + "enabled": True} + + FIELDS = ["enabled", "self_start", "next_stream", "isg", "mode", "rx_stats", "packet", "vm"] + + def __init__(self): + super(CStream, self).__init__() + for field in CStream.FIELDS: + setattr(self, field, None) + + def load_data(self, **kwargs): + for k, v in kwargs.items(): + if k == "rx_stats": + if isinstance(v, dict): + setattr(self, k, CRxStats(**v)) + elif isinstance(v, CRxStats): + setattr(self, k, v) + elif k == "mode": + if isinstance(v, dict): + setattr(self, k, CTxMode(v)) + elif isinstance(v, CTxMode): + setattr(self, k, v) + else: + setattr(self, k, v) + + + + # def __init__(self, enabled, self_start, next_stream, isg, mode, rx_stats, packet, vm): + # super(CStream, self).__init__() + # for k, v in kwargs.items(): + # if k == "rx_stats": + # if isinstance(v, dict): + # setattr(self, k, CRxStats(v)) + # elif isinstance(v, CRxStats): + # setattr(self, k, v) + # elif k == "mode": + # if isinstance(v, dict): + # setattr(self, k, CTxMode(v)) + # elif isinstance(v, CTxMode): + # setattr(self, k, v) + # else: + # 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()} + + + + + + if __name__ == "__main__": pass -- cgit From 80bd7895112cba0b3cbb6d56995def6ffbdccf33 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 18 Oct 2015 16:12:26 +0300 Subject: Progress in trex_streams and in yaml_utils. Next, start working on StreamList object --- .../trex_control_plane/common/trex_streams.py | 274 +++++++++------------ 1 file changed, 123 insertions(+), 151 deletions(-) mode change 100644 => 100755 scripts/automation/trex_control_plane/common/trex_streams.py (limited to 'scripts/automation/trex_control_plane/common') diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py old mode 100644 new mode 100755 index e6aa66f2..e366001d --- a/scripts/automation/trex_control_plane/common/trex_streams.py +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -5,13 +5,14 @@ from client_utils.packet_builder import CTRexPktBuilder from collections import OrderedDict from client_utils.yaml_utils import * import dpkt +import struct class CStreamList(object): def __init__(self): - self.streams_list = OrderedDict() - self.yaml_loader = CTRexYAMLLoader("rpc_exceptions.yaml") + self.streams_list = {OrderedDict()} + self.yaml_loader = CTRexYAMLLoader("rpc_defaults.yaml") self._stream_id = 0 # self._stream_by_name = {} @@ -62,184 +63,155 @@ class CStreamList(object): new_stream_obj.load_data(**new_stream_data) self.append_stream(stream_name, new_stream_obj) - - - # start validating and reassembling clients input - + def compile_streams(self): + stream_ids = {} pass -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) +class CRxStats(object): - @property - def seq_enabled(self): - return self._rx_dict.get("seq_enabled") + FIELDS = ["seq_enabled", "latency_enabled"] + def __init__(self, enabled=False, **kwargs): + self.enabled = bool(enabled) + for field in CRxStats.FIELDS: + setattr(self, field, kwargs.get(field, False)) - @seq_enabled.setter - def seq_enabled(self, bool_value): - self._rx_dict['seq_enabled'] = bool(bool_value) + def dump(self): + if self.enabled: + dump = {"enabled": True} + dump.update({k: getattr(self, k) + for k in CRxStats.FIELDS + if getattr(self, k) + }) + return dump + else: + return {"enabled": False} - @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 + GENERAL_FIELDS = ["type", "pps"] + FIELDS = {"continuous": [], + "single_burst": ["total_pkts"], + "multi_burst": ["pkts_per_burst", "ibg", "count"]} + + def __init__(self, type, pps=0, **kwargs): + self._MODES = CTxMode.FIELDS.keys() + self.type = type + self.pps = pps + for field in CTxMode.FIELDS.get(self.type): + setattr(self, field, kwargs.get(field, 0)) - 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)) + @property + def type(self): + return self._type + + @type.setter + def type(self, type): + if type not in self._MODES: + raise ValueError("Unknown TX mode ('{0}')has been initialized.".format(type)) + self._type = type + self._reset_fields() def dump(self): - dump = {"type": self._tx_mode} - dump.update({k: v - for k, v in self._fields.items() + dump = ({k: getattr(self, k) + for k in CTxMode.GENERAL_FIELDS + }) + dump.update({k: getattr(self, k) + for k in CTxMode.FIELDS.get(self.type) }) return dump + def _reset_fields(self): + for field in CTxMode.FIELDS.get(self.type): + setattr(self, field, 0) + + class CStream(object): """docstring for CStream""" - DEFAULTS = {"rx_stats": CRxStats, - "mode": CTxMode, - "isg": 5.0, - "next_stream": -1, - "self_start": True, - "enabled": True} FIELDS = ["enabled", "self_start", "next_stream", "isg", "mode", "rx_stats", "packet", "vm"] def __init__(self): - super(CStream, self).__init__() + self.is_loaded = False for field in CStream.FIELDS: setattr(self, field, None) def load_data(self, **kwargs): - for k, v in kwargs.items(): - if k == "rx_stats": - if isinstance(v, dict): - setattr(self, k, CRxStats(**v)) - elif isinstance(v, CRxStats): - setattr(self, k, v) - elif k == "mode": - if isinstance(v, dict): - setattr(self, k, CTxMode(v)) - elif isinstance(v, CTxMode): - setattr(self, k, v) - else: - setattr(self, k, v) - - - - # def __init__(self, enabled, self_start, next_stream, isg, mode, rx_stats, packet, vm): - # super(CStream, self).__init__() - # for k, v in kwargs.items(): - # if k == "rx_stats": - # if isinstance(v, dict): - # setattr(self, k, CRxStats(v)) - # elif isinstance(v, CRxStats): - # setattr(self, k, v) - # elif k == "mode": - # if isinstance(v, dict): - # setattr(self, k, CTxMode(v)) - # elif isinstance(v, CTxMode): - # setattr(self, k, v) - # else: - # 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) + try: + for k in CStream.FIELDS: + if k == "rx_stats": + rx_stats_data = kwargs[k] + if isinstance(rx_stats_data, dict): + setattr(self, k, CRxStats(**rx_stats_data)) + elif isinstance(rx_stats_data, CRxStats): + setattr(self, k, rx_stats_data) + elif k == "mode": + tx_mode = kwargs[k] + if isinstance(tx_mode, dict): + setattr(self, k, CTxMode(**tx_mode)) + elif isinstance(tx_mode, CTxMode): + setattr(self, k, tx_mode) + elif k == "packet": + if isinstance(kwargs[k], CTRexPktBuilder): + if "vm" not in kwargs: + self.load_packet_obj(kwargs[k]) + else: + raise ValueError("When providing packet object with a CTRexPktBuilder, vm parameter " + "should not be supplied") + else: + binary = kwargs[k]["binary"] + if isinstance(binary, list): + setattr(self, k, kwargs[k]) + elif isinstance(binary, str) and binary.endswith(".pcap"): + self.load_packet_from_pcap(binary, kwargs[k]["meta"]) + else: + raise ValueError("Packet binary attribute has been loaded with unsupported value." + "Supported values are reference to pcap file with SINGLE packet, " + "or a list of unsigned-byte integers") + else: + setattr(self, k, kwargs[k]) + self.is_loaded = True + except KeyError as e: + cause = e.args[0] + raise KeyError("The attribute '{0}' is missing as a field of the CStream object.\n" + "Loaded data must contain all of the following fields: {1}".format(cause, CStream.FIELDS)) + + def load_packet_obj(self, packet_obj): + assert isinstance(packet_obj, CTRexPktBuilder) + self.packet = packet_obj.dump_pkt() + self.vm = packet_obj.get_vm_data() + + def load_packet_from_pcap(self, pcap_path, metadata=''): + with open(pcap_path, 'r') as f: + pcap = dpkt.pcap.Reader(f) + first_packet = True + for _, buf in pcap: + # this is an iterator, can't evaluate the number of files in advance + if first_packet: + self.packet = {"binary": [struct.unpack('B', buf[i:i+1])[0] # represent data as list of 0-255 ints + for i in range(0, len(buf))], + "meta": metadata} # meta data continues without a change. + first_packet = False + else: + raise ValueError("Provided pcap file contains more than single packet.") + # arrive here ONLY if pcap contained SINGLE packet + return + def dump(self): - 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()} + if self.is_loaded: + dump = {} + for key in CStream.FIELDS: + try: + dump[key] = getattr(self, key).dump() # use dump() method of compound object, such TxMode + except AttributeError: + dump[key] = getattr(self, key) + return dump + else: + raise RuntimeError("CStream object isn't loaded with data. Use 'load_data' method.") -- cgit From d09df99769f67819c64a7a025dbdcd39811c7b44 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 20 Oct 2015 03:17:08 +0300 Subject: Major progress in console, yaml utils, and trex_streams basically done, minor changes remianing BIG ISSUE LEFT: rewire console to work with trexstateless client module --- .../trex_control_plane/common/rpc_defaults.yaml | 8 +- .../trex_control_plane/common/trex_streams.py | 85 ++++++++++++++-------- 2 files changed, 62 insertions(+), 31 deletions(-) (limited to 'scripts/automation/trex_control_plane/common') diff --git a/scripts/automation/trex_control_plane/common/rpc_defaults.yaml b/scripts/automation/trex_control_plane/common/rpc_defaults.yaml index 5816f980..32631609 100755 --- a/scripts/automation/trex_control_plane/common/rpc_defaults.yaml +++ b/scripts/automation/trex_control_plane/common/rpc_defaults.yaml @@ -41,8 +41,8 @@ stream: type: [int, double, string] has_default: YES default: 0.0 - next_stream: - type: [int, string] # string to allow naming binding + next_stream_id: + type: string # string to allow naming binding has_default: YES default: -1 # no next streams packet: @@ -101,6 +101,10 @@ rx_stats: type: boolean has_default: YES default: False + stream_id: + type: string + has_default: YES + default: False # use related stream_id seq_enabled: type: boolean has_default: YES diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py index e366001d..674a6bcc 100755 --- a/scripts/automation/trex_control_plane/common/trex_streams.py +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -2,19 +2,21 @@ import external_packages from client_utils.packet_builder import CTRexPktBuilder -from collections import OrderedDict +from collections import OrderedDict, namedtuple from client_utils.yaml_utils import * import dpkt import struct +import copy +import os +StreamPack = namedtuple('StreamPack', ['stream_id', 'stream']) class CStreamList(object): def __init__(self): - self.streams_list = {OrderedDict()} - self.yaml_loader = CTRexYAMLLoader("rpc_defaults.yaml") - self._stream_id = 0 - # self._stream_by_name = {} + self.streams_list = {} + self.yaml_loader = CTRexYAMLLoader(os.path.join(os.path.dirname(os.path.realpath(__file__)), + "rpc_defaults.yaml")) def append_stream(self, name, stream_obj): assert isinstance(stream_obj, CStream) @@ -24,21 +26,19 @@ class CStreamList(object): return def remove_stream(self, name): - return self.streams_list.pop(name) - - def rearrange_streams(self, streams_names_list, new_streams_dict={}): - tmp_list = OrderedDict() - for stream in streams_names_list: - if stream in self.streams_list: - tmp_list[stream] = self.streams_list.get(stream) - elif stream in new_streams_dict: - new_stream_obj = new_streams_dict.get(stream) - assert isinstance(new_stream_obj, CStream) - tmp_list[stream] = new_stream_obj - else: - raise NameError("Given stream named '{0}' cannot be found in existing stream list or and wasn't" - "provided with the new_stream_dict parameter.".format(stream)) - self.streams_list = tmp_list + popped = self.streams_list.pop(name) + if popped: + for stream_name, stream in self.streams_list.items(): + if stream.next_stream_id == name: + stream.next_stream_id = -1 + try: + rx_stats_stream = getattr(stream.rx_stats, "stream_id") + if rx_stats_stream == name: + # if a referenced stream of rx_stats object deleted, revert to rx stats of current stream + setattr(stream.rx_stats, "stream_id", stream_name) + except AttributeError as e: + continue # + return popped def export_to_yaml(self, file_path): raise NotImplementedError("export_to_yaml method is not implemented, yet") @@ -48,7 +48,6 @@ class CStreamList(object): self.streams_list.clear() streams_data = load_yaml_to_obj(file_path) assert isinstance(streams_data, list) - raw_streams = {} for stream in streams_data: stream_name = stream.get("name") raw_stream = stream.get("stream") @@ -62,16 +61,41 @@ class CStreamList(object): new_stream_obj = CStream() new_stream_obj.load_data(**new_stream_data) self.append_stream(stream_name, new_stream_obj) + return streams_data def compile_streams(self): + # first, assign an id to each stream stream_ids = {} - - pass + for idx, stream_name in enumerate(self.streams_list): + stream_ids[stream_name] = idx + # next, iterate over the streams and transform them from working with names to ids. + # with that build a new dict with old stream_name as the key, and StreamPack as the stored value + compiled_streams = {} + for stream_name, stream in self.streams_list.items(): + tmp_stream = CStreamList._compile_single_stream(stream_name, stream, stream_ids) + compiled_streams[stream_name] = StreamPack(stream_ids.get(stream_name), + tmp_stream) + return compiled_streams + + @staticmethod + def _compile_single_stream(stream_name, stream, id_dict): + # copy the old stream to temporary one, no change to class attributes + tmp_stream = copy.copy(stream) + next_stream_id = id_dict.get(getattr(tmp_stream, "next_stream_id"), -1) + try: + rx_stats_stream_id = id_dict.get(getattr(tmp_stream.rx_stats, "stream_id"), + id_dict.get(stream_name)) + except AttributeError as e: + rx_stats_stream_id = id_dict.get(stream_name) + # assign resolved values to stream object + tmp_stream.next_stream_id = next_stream_id + tmp_stream.rx_stats.stream_id = rx_stats_stream_id + return tmp_stream class CRxStats(object): - FIELDS = ["seq_enabled", "latency_enabled"] + FIELDS = ["seq_enabled", "latency_enabled", "stream_id"] def __init__(self, enabled=False, **kwargs): self.enabled = bool(enabled) for field in CRxStats.FIELDS: @@ -82,7 +106,7 @@ class CRxStats(object): dump = {"enabled": True} dump.update({k: getattr(self, k) for k in CRxStats.FIELDS - if getattr(self, k) + if getattr(self, k) or k == "stream_id" }) return dump else: @@ -132,10 +156,12 @@ class CTxMode(object): class CStream(object): """docstring for CStream""" - FIELDS = ["enabled", "self_start", "next_stream", "isg", "mode", "rx_stats", "packet", "vm"] + FIELDS = ["enabled", "self_start", "next_stream_id", "isg", "mode", "rx_stats", "packet", "vm"] + # COMPILE_FIELDS = ["enabled", "self_start", "next_stream_id", "isg", "mode", "rx_stats", "packet", "vm"] def __init__(self): self.is_loaded = False + self._is_compiled = False for field in CStream.FIELDS: setattr(self, field, None) @@ -201,7 +227,8 @@ class CStream(object): return - def dump(self): + def dump(self, compilation=False): + # fields = CStream.COMPILE_FIELDS if compilation else CStream.FIELDS if self.is_loaded: dump = {} for key in CStream.FIELDS: @@ -213,8 +240,8 @@ class CStream(object): else: raise RuntimeError("CStream object isn't loaded with data. Use 'load_data' method.") - - + def dump_compiled(self): + return self.dump(compilation=True) -- cgit From 5abe21ffb26a15c2a63e90b5628d704e8211b599 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 20 Oct 2015 09:12:33 +0300 Subject: + Added traffic options at stl directory + updated console to support multiplier on loading + fixed minor issues at yaml_utils and trex_streams objects + console not stable, YET --- scripts/automation/trex_control_plane/common/trex_streams.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts/automation/trex_control_plane/common') diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py index 1aeb46b0..783f2769 100755 --- a/scripts/automation/trex_control_plane/common/trex_streams.py +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -43,7 +43,7 @@ class CStreamList(object): def export_to_yaml(self, file_path): raise NotImplementedError("export_to_yaml method is not implemented, yet") - def load_yaml(self, file_path, multiplier_dict={}): + def load_yaml(self, file_path, multiplier=1): # clear all existing streams linked to this object self.streams_list.clear() streams_data = load_yaml_to_obj(file_path) @@ -57,11 +57,11 @@ class CStreamList(object): "Provided item was:\n {stream}".format(stream)) new_stream_data = self.yaml_loader.validate_yaml(raw_stream, "stream", - multiplier= multiplier_dict.get(stream_name, 1)) + multiplier= multiplier) new_stream_obj = CStream() new_stream_obj.load_data(**new_stream_data) self.append_stream(stream_name, new_stream_obj) - return streams_data + return new_stream_data def compile_streams(self): # first, assign an id to each stream -- cgit