summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Klein <danklein10@gmail.com>2015-10-15 09:57:35 +0300
committerDan Klein <danklein10@gmail.com>2015-10-15 09:57:35 +0300
commit0c5a4348a31e0e8d76dd1fcf378cb2c0a2867f59 (patch)
treedcaa9ab3c33b0e62aa41e29e6a6429194a956b71
parent2dd1a4d85c559ddafe695b6d6d393ee086e1a3de (diff)
updated yaml utils and stream object
-rwxr-xr-xscripts/automation/trex_control_plane/client_utils/external_packages.py3
-rw-r--r--scripts/automation/trex_control_plane/client_utils/yaml_utils.py118
-rwxr-xr-xscripts/automation/trex_control_plane/common/external_packages.py28
-rw-r--r--scripts/automation/trex_control_plane/common/rpc_defaults.yaml107
-rw-r--r--scripts/automation/trex_control_plane/common/trex_status.py8
-rw-r--r--scripts/automation/trex_control_plane/common/trex_streams.py190
6 files changed, 453 insertions, 1 deletions
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