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 --- .../client_utils/external_packages.py | 3 +- .../trex_control_plane/client_utils/yaml_utils.py | 118 +++++++++++++ .../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 +++++++++++++++++++++ 6 files changed, 453 insertions(+), 1 deletion(-) create mode 100644 scripts/automation/trex_control_plane/client_utils/yaml_utils.py 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') diff --git a/scripts/automation/trex_control_plane/client_utils/external_packages.py b/scripts/automation/trex_control_plane/client_utils/external_packages.py index 4b10609b..e2bb37a5 100755 --- a/scripts/automation/trex_control_plane/client_utils/external_packages.py +++ b/scripts/automation/trex_control_plane/client_utils/external_packages.py @@ -8,7 +8,8 @@ ROOT_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir)) PATH_TO_PYTHON_LIB = os.path.abspath(os.path.join(ROOT_PATH, os.pardir, os.pardir, 'external_libs')) CLIENT_UTILS_MODULES = ['zmq', - 'dpkt-1.8.6' + 'dpkt-1.8.6', + 'PyYAML-3.01/lib' ] def import_client_utils_modules(): diff --git a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py new file mode 100644 index 00000000..f04449ac --- /dev/null +++ b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py @@ -0,0 +1,118 @@ + +''' +Dan Klein +Cisco Systems, Inc. + +Copyright (c) 2015-2015 Cisco Systems, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' + +import external_packages +import yaml + +def yaml_obj_validator(evaluated_obj, yaml_reference_file_path, root_obj, fill_defaults=True): + """ + validate SINGLE ROOT object with yaml reference file. + Fills up default values if hasn't been assigned by user and available. + + :param evaluated_obj: python object that should match the yaml reference file + :param yaml_reference_file_path: + :param fill_defaults: + :return: a python representation object of the YAML file if OK + """ + type_dict = {"double":float, + "int":int, + "array":list, + "string":str, + "boolean":bool} + + def validator_rec_helper(obj_to_eval, ref_obj, root_key): + ref_item = ref_obj.get(root_key) + if ref_item is not None: + if "type" in obj_to_eval: + ref_item = ref_item[obj_to_eval.get("type")] + if isinstance(ref_item, dict) and "type" not in ref_item: # this is not a terminal + result_obj = {} + # iterate over key-value pairs + for k, v in ref_item.items(): + if k in obj_to_eval: + # need to validate with ref obj + tmp_type = v.get('type') + if tmp_type == "object": + # go deeper into nesting hierarchy + result_obj[k] = validator_rec_helper(obj_to_eval.get(k), ref_obj, k) + elif isinstance(tmp_type, list): + # item can be one of multiple types + python_types = set() + for t in tmp_type: + if t in type_dict: + python_types.add(type_dict.get(t)) + else: + raise TypeError("Unknown resolving for type {0}".format(t)) + if type(obj_to_eval[k]) not in python_types: + raise TypeError("Type of object field '{0}' is not allowed".format(k)) + + else: + # this is a single type field + python_type = type_dict.get(tmp_type) + if not isinstance(obj_to_eval[k], python_type): + raise TypeError("Type of object field '{0}' is not allowed".format(k)) + else: + # WE'RE OK! + result_obj[k] = obj_to_eval[k] + else: + # this is an object field that wasn't specified by the user + if v.get('has_default'): + # WE'RE OK! + result_obj[k] = v.get('default') + else: + # This is a mandatory field! + raise ValueError("The {0} field is mandatory and must be specified explicitly".format(v)) + return result_obj + elif isinstance(ref_item, list): + return [] + else: + + + + else: + raise KeyError("The given key is not ") + + + + + pass + pass + elif isinstance(obj_to_eval, list): + # iterate as list sequence + pass + else: + # evaluate as single item + pass + pass + + try: + yaml_ref = yaml.load(file(yaml_reference_file_path, 'r')) + result = validator_rec_helper(evaluated_obj, yaml_ref, root_obj) + + except yaml.YAMLError as e: + raise + except Exception: + raise + +def yaml_loader(file_path): + pass + +def yaml_exporter(file_path): + pass + +if __name__ == "__main__": + pass diff --git a/scripts/automation/trex_control_plane/common/external_packages.py b/scripts/automation/trex_control_plane/common/external_packages.py new file mode 100755 index 00000000..62121d4f --- /dev/null +++ b/scripts/automation/trex_control_plane/common/external_packages.py @@ -0,0 +1,28 @@ +#!/router/bin/python + +import sys +import os + +CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) +ROOT_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir)) # path to trex_control_plane directory +PATH_TO_PYTHON_LIB = os.path.abspath(os.path.join(ROOT_PATH, os.pardir, os.pardir, 'external_libs')) + +CLIENT_UTILS_MODULES = ['PyYAML-3.01/lib' + ] + +def import_common_modules(): + # must be in a higher priority + sys.path.insert(0, PATH_TO_PYTHON_LIB) + sys.path.append(ROOT_PATH) + import_module_list(CLIENT_UTILS_MODULES) + + +def import_module_list(modules_list): + assert(isinstance(modules_list, list)) + for p in modules_list: + full_path = os.path.join(PATH_TO_PYTHON_LIB, p) + fix_path = os.path.normcase(full_path) + sys.path.insert(1, full_path) + +import_common_modules() + diff --git a/scripts/automation/trex_control_plane/common/rpc_defaults.yaml b/scripts/automation/trex_control_plane/common/rpc_defaults.yaml new file mode 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/client_utils/yaml_utils.py | 291 +++++++++++++++------ .../trex_control_plane/common/rpc_defaults.yaml | 28 +- 2 files changed, 231 insertions(+), 88 deletions(-) mode change 100644 => 100755 scripts/automation/trex_control_plane/client_utils/yaml_utils.py mode change 100644 => 100755 scripts/automation/trex_control_plane/common/rpc_defaults.yaml (limited to 'scripts/automation') diff --git a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py old mode 100644 new mode 100755 index f04449ac..3ec3c9c8 --- a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py +++ b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py @@ -1,5 +1,5 @@ -''' +""" Dan Klein Cisco Systems, Inc. @@ -13,100 +13,239 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -''' +""" import external_packages import yaml -def yaml_obj_validator(evaluated_obj, yaml_reference_file_path, root_obj, fill_defaults=True): - """ - validate SINGLE ROOT object with yaml reference file. - Fills up default values if hasn't been assigned by user and available. - - :param evaluated_obj: python object that should match the yaml reference file - :param yaml_reference_file_path: - :param fill_defaults: - :return: a python representation object of the YAML file if OK - """ - type_dict = {"double":float, + +class CTRexYAMLLoader(object): + TYPE_DICT = {"double":float, "int":int, "array":list, "string":str, "boolean":bool} - def validator_rec_helper(obj_to_eval, ref_obj, root_key): - ref_item = ref_obj.get(root_key) + def __init__(self, yaml_ref_file_path): + self.yaml_path = yaml_ref_file_path + self.ref_obj = None + + def load_reference(self): + try: + self.ref_obj = yaml.load(file(self.yaml_path, 'r')) + except yaml.YAMLError as e: + raise + except Exception as e: + raise + + def check_term_param_type(self, val, val_field, ref_val, multiplier): + print val, val_field, ref_val + tmp_type = ref_val.get('type') + if isinstance(tmp_type, list): + # item can be one of multiple types + print "multiple choice!" + python_types = set() + for t in tmp_type: + if t in self.TYPE_DICT: + python_types.add(self.TYPE_DICT.get(t)) + else: + return False, TypeError("Unknown resolving for type {0}".format(t)) + print "python legit types: ", python_types + if type(val) not in python_types: + return False, TypeError("Type of object field '{0}' is not allowed".format(val_field)) + else: + # WE'RE OK! + return True, CTRexYAMLLoader._calc_final_value(val, multiplier, ref_val.get('multiply', False)) + else: + # this is a single type field + python_type = self.TYPE_DICT.get(tmp_type) + if not isinstance(val, python_type): + return False, TypeError("Type of object field '{0}' is not allowed".format(val_field)) + else: + # WE'RE OK! + return True, CTRexYAMLLoader._calc_final_value(val, multiplier, ref_val.get('multiply', False)) + + def get_reference_default(self, root_obj, sub_obj, key): + print root_obj, sub_obj, key + if sub_obj: + ref_field = self.ref_obj.get(root_obj).get(sub_obj).get(key) + else: + ref_field = self.ref_obj.get(root_obj).get(key) + if 'has_default' in ref_field: + if ref_field.get('has_default'): + # WE'RE OK! + return True, ref_field.get('default') + else: + # This is a mandatory field! + return False, ValueError("The {0} field is mandatory and must be specified explicitly".format(key)) + else: + return False, ValueError("The {0} field has no indication about default value".format(key)) + + def validate_yaml(self, evaluated_obj, root_obj, fill_defaults=True, multiplier=1): + if isinstance(evaluated_obj, dict) and evaluated_obj.keys() == [root_obj]: + evaluated_obj = evaluated_obj.get(root_obj) + if not self.ref_obj: + self.load_reference() + ref_item = self.ref_obj.get(root_obj) if ref_item is not None: - if "type" in obj_to_eval: - ref_item = ref_item[obj_to_eval.get("type")] - if isinstance(ref_item, dict) and "type" not in ref_item: # this is not a terminal - result_obj = {} - # iterate over key-value pairs - for k, v in ref_item.items(): - if k in obj_to_eval: - # need to validate with ref obj - tmp_type = v.get('type') - if tmp_type == "object": - # go deeper into nesting hierarchy - result_obj[k] = validator_rec_helper(obj_to_eval.get(k), ref_obj, k) - elif isinstance(tmp_type, list): - # item can be one of multiple types - python_types = set() - for t in tmp_type: - if t in type_dict: - python_types.add(type_dict.get(t)) + try: + typed_obj = [False, None] # first item stores validity (multiple object "shapes"), second stored type + if "type" in evaluated_obj: + ref_item = ref_item[evaluated_obj.get("type")] + print "lower resolution with typed object" + typed_obj = [True, evaluated_obj.get("type")] + if isinstance(ref_item, dict) and "type" not in ref_item: # this is not a terminal + result_obj = {} + if typed_obj[0]: + result_obj["type"] = typed_obj[1] + print "processing dictionary non-terminal value" + for k, v in ref_item.items(): + print "processing element '{0}' with value '{1}'".format(k,v) + if k in evaluated_obj: + # validate with ref obj + print "found in evaluated object!" + tmp_type = v.get('type') + print tmp_type + print evaluated_obj + if tmp_type == "object": + # go deeper into nesting hierarchy + print "This is an object type, recursion!" + result_obj[k] = self.validate_yaml(evaluated_obj.get(k), k, fill_defaults, multiplier) + else: + # validation on terminal type + print "Validating terminal type %s" % k + res_ok, data = self.check_term_param_type(evaluated_obj.get(k), k, v, multiplier) + print "Validating: ", res_ok + if res_ok: + # data field contains the value to save + result_obj[k] = data else: - raise TypeError("Unknown resolving for type {0}".format(t)) - if type(obj_to_eval[k]) not in python_types: - raise TypeError("Type of object field '{0}' is not allowed".format(k)) - - else: - # this is a single type field - python_type = type_dict.get(tmp_type) - if not isinstance(obj_to_eval[k], python_type): - raise TypeError("Type of object field '{0}' is not allowed".format(k)) + # data var contains the exception to throw + raise data + elif fill_defaults: + # complete missing values with default value, if exists + sub_obj = typed_obj[1] if typed_obj[0] else None + res_ok, data = self.get_reference_default(root_obj, sub_obj, k) + if res_ok: + # data field contains the value to save + result_obj[k] = data else: - # WE'RE OK! - result_obj[k] = obj_to_eval[k] - else: - # this is an object field that wasn't specified by the user - if v.get('has_default'): - # WE'RE OK! - result_obj[k] = v.get('default') - else: - # This is a mandatory field! - raise ValueError("The {0} field is mandatory and must be specified explicitly".format(v)) - return result_obj - elif isinstance(ref_item, list): - return [] - else: + # data var contains the exception to throw + raise data + return result_obj + elif isinstance(ref_item, list): + # currently not handling list objects + return NotImplementedError("List object are currently unsupported") + else: + raise TypeError("Unknown parse tree object type.") + except KeyError as e: + raise + else: + raise KeyError("The given root_key '{key}' does not exists on reference object".format(key=root_obj)) + @staticmethod + def _calc_final_value(val, multiplier, multiply): + if multiply: + return (val * multiplier) + else: + return val - else: - raise KeyError("The given key is not ") - pass - pass - elif isinstance(obj_to_eval, list): - # iterate as list sequence - pass - else: - # evaluate as single item - pass - pass - - try: - yaml_ref = yaml.load(file(yaml_reference_file_path, 'r')) - result = validator_rec_helper(evaluated_obj, yaml_ref, root_obj) - - except yaml.YAMLError as e: - raise - except Exception: - raise +# +# def yaml_obj_validator(evaluated_obj, yaml_reference_file_path, root_obj, fill_defaults=True): +# """ +# validate SINGLE ROOT object with yaml reference file. +# Fills up default values if hasn't been assigned by user and available. +# +# :param evaluated_obj: python object that should match the yaml reference file +# :param yaml_reference_file_path: +# :param fill_defaults: +# :return: a python representation object of the YAML file if OK +# """ +# type_dict = {"double":float, +# "int":int, +# "array":list, +# "string":str, +# "boolean":bool} +# +# def validator_rec_helper(obj_to_eval, ref_obj, root_key): +# ref_item = ref_obj.get(root_key) +# if ref_item is not None: +# if "type" in obj_to_eval: +# ref_item = ref_item[obj_to_eval.get("type")] +# if isinstance(ref_item, dict) and "type" not in ref_item: # this is not a terminal +# result_obj = {} +# # iterate over key-value pairs +# for k, v in ref_item.items(): +# if k in obj_to_eval: +# # need to validate with ref obj +# tmp_type = v.get('type') +# if tmp_type == "object": +# # go deeper into nesting hierarchy +# result_obj[k] = validator_rec_helper(obj_to_eval.get(k), ref_obj, k) +# elif isinstance(tmp_type, list): +# # item can be one of multiple types +# python_types = set() +# for t in tmp_type: +# if t in type_dict: +# python_types.add(type_dict.get(t)) +# else: +# raise TypeError("Unknown resolving for type {0}".format(t)) +# if type(obj_to_eval[k]) not in python_types: +# raise TypeError("Type of object field '{0}' is not allowed".format(k)) +# +# else: +# # this is a single type field +# python_type = type_dict.get(tmp_type) +# if not isinstance(obj_to_eval[k], python_type): +# raise TypeError("Type of object field '{0}' is not allowed".format(k)) +# else: +# # WE'RE OK! +# result_obj[k] = obj_to_eval[k] +# else: +# # this is an object field that wasn't specified by the user +# if v.get('has_default'): +# # WE'RE OK! +# result_obj[k] = v.get('default') +# else: +# # This is a mandatory field! +# raise ValueError("The {0} field is mandatory and must be specified explicitly".format(v)) +# return result_obj +# +# elif isinstance(ref_item, list): +# return [] +# else: +# +# +# +# else: +# raise KeyError("The given key is not ") +# +# +# +# +# pass +# pass +# elif isinstance(obj_to_eval, list): +# # iterate as list sequence +# pass +# else: +# # evaluate as single item +# pass +# pass +# +# try: +# yaml_ref = yaml.load(file(yaml_reference_file_path, 'r')) +# result = validator_rec_helper(evaluated_obj, yaml_ref, root_obj) +# +# except yaml.YAMLError as e: +# raise +# except Exception: +# raise def yaml_loader(file_path): pass 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/client_utils/yaml_utils.py | 115 +---------- .../trex_control_plane/common/rpc_defaults.yaml | 2 +- .../trex_control_plane/common/trex_streams.py | 230 +++++++++++++-------- 3 files changed, 154 insertions(+), 193 deletions(-) (limited to 'scripts/automation') diff --git a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py index 3ec3c9c8..0d808880 100755 --- a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py +++ b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py @@ -30,14 +30,6 @@ class CTRexYAMLLoader(object): self.yaml_path = yaml_ref_file_path self.ref_obj = None - def load_reference(self): - try: - self.ref_obj = yaml.load(file(self.yaml_path, 'r')) - except yaml.YAMLError as e: - raise - except Exception as e: - raise - def check_term_param_type(self, val, val_field, ref_val, multiplier): print val, val_field, ref_val tmp_type = ref_val.get('type') @@ -85,7 +77,7 @@ class CTRexYAMLLoader(object): if isinstance(evaluated_obj, dict) and evaluated_obj.keys() == [root_obj]: evaluated_obj = evaluated_obj.get(root_obj) if not self.ref_obj: - self.load_reference() + self.ref_obj = load_yaml_to_obj(self.yaml_path) # load reference object to class attribute. ref_item = self.ref_obj.get(root_obj) if ref_item is not None: try: @@ -151,104 +143,13 @@ class CTRexYAMLLoader(object): return val - - - - -# -# def yaml_obj_validator(evaluated_obj, yaml_reference_file_path, root_obj, fill_defaults=True): -# """ -# validate SINGLE ROOT object with yaml reference file. -# Fills up default values if hasn't been assigned by user and available. -# -# :param evaluated_obj: python object that should match the yaml reference file -# :param yaml_reference_file_path: -# :param fill_defaults: -# :return: a python representation object of the YAML file if OK -# """ -# type_dict = {"double":float, -# "int":int, -# "array":list, -# "string":str, -# "boolean":bool} -# -# def validator_rec_helper(obj_to_eval, ref_obj, root_key): -# ref_item = ref_obj.get(root_key) -# if ref_item is not None: -# if "type" in obj_to_eval: -# ref_item = ref_item[obj_to_eval.get("type")] -# if isinstance(ref_item, dict) and "type" not in ref_item: # this is not a terminal -# result_obj = {} -# # iterate over key-value pairs -# for k, v in ref_item.items(): -# if k in obj_to_eval: -# # need to validate with ref obj -# tmp_type = v.get('type') -# if tmp_type == "object": -# # go deeper into nesting hierarchy -# result_obj[k] = validator_rec_helper(obj_to_eval.get(k), ref_obj, k) -# elif isinstance(tmp_type, list): -# # item can be one of multiple types -# python_types = set() -# for t in tmp_type: -# if t in type_dict: -# python_types.add(type_dict.get(t)) -# else: -# raise TypeError("Unknown resolving for type {0}".format(t)) -# if type(obj_to_eval[k]) not in python_types: -# raise TypeError("Type of object field '{0}' is not allowed".format(k)) -# -# else: -# # this is a single type field -# python_type = type_dict.get(tmp_type) -# if not isinstance(obj_to_eval[k], python_type): -# raise TypeError("Type of object field '{0}' is not allowed".format(k)) -# else: -# # WE'RE OK! -# result_obj[k] = obj_to_eval[k] -# else: -# # this is an object field that wasn't specified by the user -# if v.get('has_default'): -# # WE'RE OK! -# result_obj[k] = v.get('default') -# else: -# # This is a mandatory field! -# raise ValueError("The {0} field is mandatory and must be specified explicitly".format(v)) -# return result_obj -# -# elif isinstance(ref_item, list): -# return [] -# else: -# -# -# -# else: -# raise KeyError("The given key is not ") -# -# -# -# -# pass -# pass -# elif isinstance(obj_to_eval, list): -# # iterate as list sequence -# pass -# else: -# # evaluate as single item -# pass -# pass -# -# try: -# yaml_ref = yaml.load(file(yaml_reference_file_path, 'r')) -# result = validator_rec_helper(evaluated_obj, yaml_ref, root_obj) -# -# except yaml.YAMLError as e: -# raise -# except Exception: -# raise - -def yaml_loader(file_path): - pass +def load_yaml_to_obj(file_path): + try: + return yaml.load(file(file_path, 'r')) + except yaml.YAMLError as e: + raise + except Exception as e: + raise def yaml_exporter(file_path): pass 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 --- .../client_utils/packet_builder.py | 6 +- .../trex_control_plane/client_utils/yaml_utils.py | 3 +- .../trex_control_plane/common/trex_streams.py | 274 +++++++++------------ 3 files changed, 129 insertions(+), 154 deletions(-) mode change 100644 => 100755 scripts/automation/trex_control_plane/common/trex_streams.py (limited to 'scripts/automation') diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py index c687126b..caa6eab3 100755 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -33,6 +33,7 @@ class CTRexPktBuilder(object): self._max_pkt_size = max_pkt_size self.payload_gen = CTRexPktBuilder.CTRexPayloadGen(self._packet, self._max_pkt_size) self.vm = CTRexPktBuilder.CTRexVM() + self.metadata = "" def add_pkt_layer(self, layer_name, pkt_layer): """ @@ -441,8 +442,9 @@ class CTRexPktBuilder(object): if self._packet is None: raise CTRexPktBuilder.EmptyPacketError() pkt_in_hex = binascii.hexlify(str(self._packet)) - return [int(pkt_in_hex[i:i+2], 16) - for i in range(0, len(pkt_in_hex), 2)] + return {"binary": [int(pkt_in_hex[i:i+2], 16) + for i in range(0, len(pkt_in_hex), 2)], + "meta": self.metadata} # return [pkt_in_hex[i:i+2] for i in range(0, len(pkt_in_hex), 2)] def dump_pkt_to_pcap(self, file_path, ts=None): diff --git a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py index 0d808880..024b73c2 100755 --- a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py +++ b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py @@ -77,7 +77,8 @@ class CTRexYAMLLoader(object): if isinstance(evaluated_obj, dict) and evaluated_obj.keys() == [root_obj]: evaluated_obj = evaluated_obj.get(root_obj) if not self.ref_obj: - self.ref_obj = load_yaml_to_obj(self.yaml_path) # load reference object to class attribute. + self.ref_obj = load_yaml_to_any_obj(self.yaml_path) + # self.load_reference() ref_item = self.ref_obj.get(root_obj) if ref_item is not None: try: 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 --- .../client_utils/jsonrpc_client.py | 81 ++++++++++++-------- .../client_utils/packet_builder.py | 2 +- .../trex_control_plane/client_utils/yaml_utils.py | 28 +++---- .../trex_control_plane/common/rpc_defaults.yaml | 8 +- .../trex_control_plane/common/trex_streams.py | 85 +++++++++++++-------- .../trex_control_plane/console/trex_console.py | 86 ++++++++++++++++++++++ 6 files changed, 215 insertions(+), 75 deletions(-) mode change 100644 => 100755 scripts/automation/trex_control_plane/console/trex_console.py (limited to 'scripts/automation') 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 8c8987b6..b2d83cff 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -458,32 +458,55 @@ class TrexStatelessClient(JsonRpcClient): return snap # add stream - def add_stream (self, port_id, stream_id, isg, next_stream_id, packet): - if not port_id in self.get_owned_ports(): - return False, "Port {0} is not owned... please take ownership before adding streams".format(port_id) - - handler = self.port_handlers[port_id] - - stream = {} - stream['enabled'] = True - stream['self_start'] = True - stream['isg'] = isg - stream['next_stream_id'] = next_stream_id - stream['packet'] = {} - stream['packet']['binary'] = packet - stream['packet']['meta'] = "" - stream['vm'] = [] - stream['rx_stats'] = {} - stream['rx_stats']['enabled'] = False - - stream['mode'] = {} - stream['mode']['type'] = 'continuous' - stream['mode']['pps'] = 10.0 - - params = {} - params['handler'] = handler - params['stream'] = stream - params['port_id'] = port_id - params['stream_id'] = stream_id - - return self.invoke_rpc_method('add_stream', params = params) + # def add_stream (self, port_id, stream_id, isg, next_stream_id, packet, vm=[]): + # if not port_id in self.get_owned_ports(): + # return False, "Port {0} is not owned... please take ownership before adding streams".format(port_id) + # + # handler = self.port_handlers[port_id] + # + # stream = {} + # stream['enabled'] = True + # stream['self_start'] = True + # stream['isg'] = isg + # stream['next_stream_id'] = next_stream_id + # stream['packet'] = {} + # stream['packet']['binary'] = packet + # stream['packet']['meta'] = "" + # stream['vm'] = vm + # stream['rx_stats'] = {} + # stream['rx_stats']['enabled'] = False + # + # stream['mode'] = {} + # stream['mode']['type'] = 'continuous' + # stream['mode']['pps'] = 10.0 + # + # params = {} + # params['handler'] = handler + # params['stream'] = stream + # params['port_id'] = port_id + # params['stream_id'] = stream_id + # + # print params + # return self.invoke_rpc_method('add_stream', params = params) + + def add_stream(self, port_id_array, stream_pack_list): + batch = self.create_batch() + + for port_id in port_id_array: + for stream_pack in stream_pack_list: + params = {"port_id": port_id, + "handler": self.port_handlers[port_id], + "stream_id": stream_pack.stream_id, + "stream": stream_pack.stream} + batch.add("add_stream", params=params) + rc, resp_list = batch.invoke() + if not rc: + return rc, resp_list + + for i, rc in enumerate(resp_list): + if rc[0]: + self.port_handlers[port_id_array[i]] = rc[1] + + return True, resp_list + + # return self.invoke_rpc_method('add_stream', params = params) diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py index caa6eab3..3aeb6a34 100755 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -889,7 +889,7 @@ class CTRexPktBuilder(object): dictionary holds variable data of VM variable """ - return {"ins_name": "flow_var", # VM variable dump always refers to manipulate instruction. + return {"type": "flow_var", # VM variable dump always refers to manipulate instruction. "name": self.name, "size": self.size, "op": self.operation, diff --git a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py index 024b73c2..414744fc 100755 --- a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py +++ b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py @@ -31,18 +31,18 @@ class CTRexYAMLLoader(object): self.ref_obj = None def check_term_param_type(self, val, val_field, ref_val, multiplier): - print val, val_field, ref_val + # print val, val_field, ref_val tmp_type = ref_val.get('type') if isinstance(tmp_type, list): # item can be one of multiple types - print "multiple choice!" + # print "multiple choice!" python_types = set() for t in tmp_type: if t in self.TYPE_DICT: python_types.add(self.TYPE_DICT.get(t)) else: return False, TypeError("Unknown resolving for type {0}".format(t)) - print "python legit types: ", python_types + # print "python legit types: ", python_types if type(val) not in python_types: return False, TypeError("Type of object field '{0}' is not allowed".format(val_field)) else: @@ -58,7 +58,7 @@ class CTRexYAMLLoader(object): return True, CTRexYAMLLoader._calc_final_value(val, multiplier, ref_val.get('multiply', False)) def get_reference_default(self, root_obj, sub_obj, key): - print root_obj, sub_obj, key + # print root_obj, sub_obj, key if sub_obj: ref_field = self.ref_obj.get(root_obj).get(sub_obj).get(key) else: @@ -77,7 +77,7 @@ class CTRexYAMLLoader(object): if isinstance(evaluated_obj, dict) and evaluated_obj.keys() == [root_obj]: evaluated_obj = evaluated_obj.get(root_obj) if not self.ref_obj: - self.ref_obj = load_yaml_to_any_obj(self.yaml_path) + self.ref_obj = load_yaml_to_obj(self.yaml_path) # self.load_reference() ref_item = self.ref_obj.get(root_obj) if ref_item is not None: @@ -85,30 +85,30 @@ class CTRexYAMLLoader(object): typed_obj = [False, None] # first item stores validity (multiple object "shapes"), second stored type if "type" in evaluated_obj: ref_item = ref_item[evaluated_obj.get("type")] - print "lower resolution with typed object" + # print "lower resolution with typed object" typed_obj = [True, evaluated_obj.get("type")] if isinstance(ref_item, dict) and "type" not in ref_item: # this is not a terminal result_obj = {} if typed_obj[0]: result_obj["type"] = typed_obj[1] - print "processing dictionary non-terminal value" + # print "processing dictionary non-terminal value" for k, v in ref_item.items(): - print "processing element '{0}' with value '{1}'".format(k,v) + # print "processing element '{0}' with value '{1}'".format(k,v) if k in evaluated_obj: # validate with ref obj - print "found in evaluated object!" + # print "found in evaluated object!" tmp_type = v.get('type') - print tmp_type - print evaluated_obj + # print tmp_type + # print evaluated_obj if tmp_type == "object": # go deeper into nesting hierarchy - print "This is an object type, recursion!" + # print "This is an object type, recursion!" result_obj[k] = self.validate_yaml(evaluated_obj.get(k), k, fill_defaults, multiplier) else: # validation on terminal type - print "Validating terminal type %s" % k + # print "Validating terminal type %s" % k res_ok, data = self.check_term_param_type(evaluated_obj.get(k), k, v, multiplier) - print "Validating: ", res_ok + # print "Validating: ", res_ok if res_ok: # data field contains the value to save result_obj[k] = data 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) diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py old mode 100644 new mode 100755 index 9478db5a..945bb177 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -9,9 +9,13 @@ import string import sys import trex_root_path +from common.trex_streams import * from client_utils.jsonrpc_client import TrexStatelessClient import trex_status +from collections import namedtuple + +LoadedStreamList = namedtuple('LoadedStreamList', ['loaded', 'compiled']) class TrexConsole(cmd.Cmd): """Trex Console""" @@ -29,6 +33,8 @@ class TrexConsole(cmd.Cmd): self.verbose = False self.postcmd(False, "") + + self.user_streams = {} # a cool hack - i stole this function and added space @@ -312,6 +318,86 @@ class TrexConsole(cmd.Cmd): print "{:<30} {:<30}".format(cmd + " - ", help) + def do_load_stream_list(self, line): + '''Loads a YAML stream list serialization into user console \n''' + args = line.split() + if args >= 2: + name = args[0] + yaml_path = args[1] + stream_list = CStreamList() + loaded_obj = stream_list.load_yaml(yaml_path) + # print self.rpc_client.pretty_json(json.dumps(loaded_obj)) + if name in self.user_streams: + print "Picked name already exist. Please pick another name." + else: + try: + compiled_streams = stream_list.compile_streams() + self.user_streams[name] = LoadedStreamList(loaded_obj, + [StreamPack(v.stream_id, v.stream.dump_compiled()) + for k, v in compiled_streams.items()]) + + print "Stream '{0}' loaded successfully".format(name) + except Exception as e: + raise + return + else: + print "please provide load name and YAML path, separated by space." + + def do_show_stream_list(self, line): + '''Shows the loaded stream list named [name] \n''' + args = line.split() + if args: + list_name = args[0] + try: + stream = self.user_streams[list_name] + if len(args) >= 2 and args[1] == "full": + print self.rpc_client.pretty_json(json.dumps(stream.compiled)) + else: + print self.rpc_client.pretty_json(json.dumps(stream.loaded)) + except KeyError as e: + print "Unknown stream list name provided" + else: + print "\nAvailable stream lists:\n{0}".format(', '.join([x + for x in self.user_streams.keys()])) + + def complete_show_stream_list (self, text, line, begidx, endidx): + return [x + for x in self.user_streams.keys() + if x.startswith(text)] + + def do_attach(self, line): + args = line.split() + if len(args) >= 1: + try: + stream_list = self.user_streams[args[0]] + port_list = self.parse_ports_from_line(' '.join(args[1:])) + owned = set(self.rpc_client.get_owned_ports()) + if set(port_list).issubset(owned): + rc, resp_list = self.rpc_client.add_stream(port_list, stream_list.compiled) + if not rc: + print "\n*** " + resp_list + "\n" + return + else: + print "Not all desired ports are aquired.\n" \ + "Acquired ports are: {acq}\n" \ + "Requested ports: {req}\n" \ + "Missing ports: {miss}".format(acq=list(owned), + req=port_list, + miss=list(set(port_list).difference(owned))) + except KeyError as e: + cause = e.args[0] + print "Provided stream list name '{0}' doesn't exists.".format(cause) + else: + print "Please provide list name and ports to attach to, or leave empty to attach to all ports." + + + + + + + + + # do #def do_snapshot (self, line): -- 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 --- .../trex_control_plane/client_utils/yaml_utils.py | 8 +++++-- .../trex_control_plane/common/trex_streams.py | 6 ++--- .../trex_control_plane/console/trex_console.py | 28 +++++++++++++++++++--- 3 files changed, 34 insertions(+), 8 deletions(-) (limited to 'scripts/automation') diff --git a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py index 414744fc..60630a04 100755 --- a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py +++ b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py @@ -108,7 +108,6 @@ class CTRexYAMLLoader(object): # validation on terminal type # print "Validating terminal type %s" % k res_ok, data = self.check_term_param_type(evaluated_obj.get(k), k, v, multiplier) - # print "Validating: ", res_ok if res_ok: # data field contains the value to save result_obj[k] = data @@ -138,8 +137,13 @@ class CTRexYAMLLoader(object): @staticmethod def _calc_final_value(val, multiplier, multiply): + def to_num(s): + try: + return int(s) + except ValueError: + return float(s) if multiply: - return (val * multiplier) + return val * to_num(multiplier) else: return val 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 diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 688f80f3..27a5eeab 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -1,5 +1,22 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- + +""" +Dan Klein, Itay Marom +Cisco Systems, Inc. + +Copyright (c) 2015-2015 Cisco Systems, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + import cmd import json import ast @@ -416,8 +433,12 @@ class TrexConsole(cmd.Cmd): if args >= 2: name = args[0] yaml_path = args[1] + try: + multiplier = args[2] + except IndexError: + multiplier = 1 stream_list = CStreamList() - loaded_obj = stream_list.load_yaml(yaml_path) + loaded_obj = stream_list.load_yaml(yaml_path, multiplier) # print self.rpc_client.pretty_json(json.dumps(loaded_obj)) if name in self.user_streams: print "Picked name already exist. Please pick another name." @@ -433,7 +454,8 @@ class TrexConsole(cmd.Cmd): raise return else: - print "please provide load name and YAML path, separated by space." + print "please provide load name and YAML path, separated by space.\n" \ + "Optionally, you may provide a third argument to specify multiplier." def do_show_stream_list(self, line): '''Shows the loaded stream list named [name] \n''' -- cgit